admin 管理员组

文章数量: 1086019

I'm trying to get my head around how nested for loops work with multidimensional arrays in JavaScipt and I'm a bit stuck on one point. Using a stock example

var arr = [[1,2], [3,4], [5,6]];
for (var i=0; i < arr.length; i++) {
    for (var j=0; j < arr[i].length; j++) {
        console.log(arr[i][j]);
    }
}

This outputs 1 2 3 4 5 6 which is what I expected. However if I add numbers to the end of the outer array:

var arr = [[1,2], [3,4], [5,6], 7, 8];
for (var i=0; i < arr.length; i++) {
    for (var j=0; j < arr[i].length; j++) {
        console.log(arr[i][j]);
    }
}

I still get the same output of 1 2 3 4 5 6 ? ? I am confused why 7 & 8 are not being picked up by the loop. Interestingly, if I use strings instead:

var arr = [["a","b"], ["c","d"], "y", "z"];
for (var i=0; i < arr.length; i++) {
    for (var j=0; j < arr[i].length; j++) {
        console.log(arr[i][j]);
    }
}

The output is a b c d y z, which is what I expected. Why does it behave differently for strings?

I'm trying to get my head around how nested for loops work with multidimensional arrays in JavaScipt and I'm a bit stuck on one point. Using a stock example

var arr = [[1,2], [3,4], [5,6]];
for (var i=0; i < arr.length; i++) {
    for (var j=0; j < arr[i].length; j++) {
        console.log(arr[i][j]);
    }
}

This outputs 1 2 3 4 5 6 which is what I expected. However if I add numbers to the end of the outer array:

var arr = [[1,2], [3,4], [5,6], 7, 8];
for (var i=0; i < arr.length; i++) {
    for (var j=0; j < arr[i].length; j++) {
        console.log(arr[i][j]);
    }
}

I still get the same output of 1 2 3 4 5 6 ? ? I am confused why 7 & 8 are not being picked up by the loop. Interestingly, if I use strings instead:

var arr = [["a","b"], ["c","d"], "y", "z"];
for (var i=0; i < arr.length; i++) {
    for (var j=0; j < arr[i].length; j++) {
        console.log(arr[i][j]);
    }
}

The output is a b c d y z, which is what I expected. Why does it behave differently for strings?

Share Improve this question edited Feb 24, 2018 at 14:10 James Z 12.3k10 gold badges27 silver badges47 bronze badges asked Feb 24, 2018 at 0:47 quietplacequietplace 5391 gold badge5 silver badges16 bronze badges 6
  • You loop looks for the elements of an arrays within an array. 7 and 8 on their own are not arrays. You neet to add brackets, either var arr = [[1,2], [3,4], [5,6], [7, 8]]; or var arr = [[1,2], [3,4], [5,6], [7, 0], [8, 0]]; – cybernetic.nomad Commented Feb 24, 2018 at 0:53
  • mmm good question btw on the letter part – Willem van der Veen Commented Feb 24, 2018 at 0:57
  • gonna look into that also – Willem van der Veen Commented Feb 24, 2018 at 0:57
  • jow I got it if you have a string and call the zero index on it it returns itself – Willem van der Veen Commented Feb 24, 2018 at 1:03
  • 2 Thanks for all the prompt and helpful answers. It will probably take me a little time to absorb this info. So, to clarify that I'm on the right track: (A) Using the nested loop means the code is only looking for nested arrays which is why if I use a single loop on this array everything is logged from the outer array level as expected [1,2] [3,4] [5,6] 7 8. (B) It works with the strings because they are "iterable" and the length property can be applied to them (i.e they can be indexed). – quietplace Commented Feb 24, 2018 at 3:17
 |  Show 1 more ment

5 Answers 5

Reset to default 2

As others have mentioned, your inner loop is all about iterating the arrays found in the top level (non-nested array). It makes the assumption that all the elements in the top-level array will be nested arrays, which isn't the case. (You need to make sure that you have an array before attempting to iterate through it.) Since 7 and 8 in the top level aren't arrays, arr[i].length returns undefined for numbers, but strings are "array like" objects and do have a length property. A string of "y" has a length of 1 and so the inner loop works because it starts from zero and obtains the character at position zero in the string "array" of "y", which is "y".

But, this is a good reason not to use traditional for loops with arrays when we now have Array.forEach(), which eliminates the need for indexes to be manually managed and allows us to access the value being enumerated directly without worrying about indexes.

var arr = [[1,2], [3,4], [5,6], 7, 8];
var output = "";

// Enumerate the top-level array:
arr.forEach(function(value){
  // Check to see if the item is an array
  if(value instanceof Array){
    // If so, enuerate that nested array
    value.forEach(function(nestedValue){
      // Add the nested array's value to the output
      output += nestedValue + " " ;
    });
  } else {
    // Item is not an array, just add its value to the output
    output += value + " ";
  }
});

console.log(output);

Oh, and by the way, I realize that this is not what you were asking about, but just as an FYI, here's a way to obtain all the values without any loops:

console.log([[1,2], [3,4], [5,6], 7, 8].toString().split(",").join(" "));

String, Array, TypedArray, Map and Set are all built-in iterables, because each of their prototype objects implements an @@iterator method. While Number is not iterable:

const iterate = v => {
  for (var i = 0; i < v.length; i++) console.log(v[i])
}

iterate([1, 'two', 777]) // iterates by element
iterate('abc') // iterates by symbol
iterate(123) // does not iterate

Here's how things look like in modern Javascript.

In respect to loops, all values can be divided into "iterable" and "non-iterable". Iterable are values that you can well... iterate - with the for..of loop.

for (let item of someIterableThing)
    // use item

(You do not use bare for loops - for(var i...i < length) - for the iteration, because not every iterable has length and indexes.)

Conversely, if you do for...of with a non-iterable thing, you'll get an error.

Arrays and strings are examples of iterable values, numbers are non-iterable. So when you have

[ [1,2], [3,4], "foobar" ]

all items in this array are iterable and your nested loop will work. However, in

[ [1,2], [3,4], 999]

the last item is non-iterable, and the nested loop will fail.

There's no built-in way to tell if an unknown value is iterable, you have to write a function for this:

 let isIterable = x => x && x[Symbol.iterator]

(see the dedicated topic).

Then, you can use the nested loop in a safe manner:

for (let item of array)
    if (isIterable(item))
        for (let subItem of item)
            console.log(subItem)
    else
        console.log(item)

As a side note, there are lots of obsolete information about Javascript on the net. The language is evolving, and things that were fine 5 years ago, are considered bad practice nowadays. Unfortunately, most tutorials, books and teachers do not keep up, and still promote old practices, like using bare for loops.

(Since people asked why exactly bare for loops are bad, consider this example:

You have an array text containing strings and multiple functions that process this array. Programmer A writes these functions in the old-fashioned manner:

for (var i = 0; i < text.length; i++)  
     do_something_with(text[i]) // ;(

Programmer B writes them in the modern way:

for (let str of text)  
     do_something_with(str) // :)

Now, the text grows bigger and bigger and doesn't fit in memory anymore. So the system architect decided to replace it with a streamable file object that only yields one string at a time. Programmer A now has to rewrite all his 100 functions to adapt to the new interface:

  for (var file = TextFile; !file.eof(); file.getNext())
      do_something_with(file.currentLine)

which involves lots of pain, convulsions and headache.

Programmer B just enjoys her vacation.)

You have a double loop. When you are doing the statement console.log(arr[i][j]); you try first to go into the index i of the arr array in this part arr[i].

You then proceed to call the index of the value in the array with [j]. Because 7, and 8 are just numbers and not arrays they don't log.

This is the reason why the letters are logged:

 var arr = [["a","b"], ["c","d"], "y", "z"];
 for (var i=0; i < arr.length; i++) {
   for (var j=0; j < arr[i].length; j++) {
/* 		console.log(arr[i][j]);  */
  }
}

var foo = "a";
var bar = foo[0];

console.log(bar);

Strings behave as arrays of characters that's why you get the letter returns itself when you call foo[0] it returns a.

Why does it behave differently for strings?

Because strings are treated as an array of characters.

A JavaScript string stores a series of characters like "John Doe". Each character can be accessed using an array index, and also has length method for getting the size of the string. Hence why your "y", "z" works but not 7, 8 as they are not an array.

本文标签: javascriptNested for loops and multidimensional arraysStack Overflow