JavaScript中多维数组的for循环

52

从现在开始,我使用这个循环来迭代数组的元素,即使我将具有各种属性的对象放入其中也可以正常工作。

var cubes[];

for (i in cubes){
     cubes[i].dimension
     cubes[i].position_x
     ecc..
}

现在,假设cubes[]是这样声明的

var cubes[][];

我能用JavaScript实现这个吗?然后如何自动迭代?

cubes[0][0]
cubes[0][1]
cubes[0][2]
cubes[1][0]
cubes[1][1]
cubes[1][2]
cubes[2][0]
ecc...

作为一种解决方法,我可以声明:

var cubes[];
var cubes1[];

并分别使用这两个数组进行操作。这是更好的解决方案吗?

9个回答

78
你可以像这样做:

var cubes = [
 [1, 2, 3],
 [4, 5, 6],    
 [7, 8, 9],
];

for(var i = 0; i < cubes.length; i++) {
    var cube = cubes[i];
    for(var j = 0; j < cube.length; j++) {
        display("cube[" + i + "][" + j + "] = " + cube[j]);
    }
}

可用的 jsFiddle:

以上内容的输出结果:

cube[0][0] = 1
cube[0][1] = 2
cube[0][2] = 3
cube[1][0] = 4
cube[1][1] = 5
cube[1][2] = 6
cube[2][0] = 7
cube[2][1] = 8
cube[2][2] = 9

1
你的解决方案只适用于二维数组,但我已经找到了一种可以循环遍历任意维度数组的方法:https://dev59.com/FnDYa4cB1Zd3GeqPCJ3z#15854485 - Anderson Green
3
@AndersonGreen OP特别寻找二维数组,因此上面的回答是针对这个问题的。感谢提供链接,希望能帮助将来寻找类似解决方案的人! - icyrock.com
1
@Bharadwaj 这是一个全新的问题 - 参见这个链接:https://dev59.com/k2855IYBdhLWcg3wc0Dy - icyrock.com
使用 ES6,可以使用 forEach() 更短地重写。请参考我的回答中的示例。 - 0stone0

24
var cubes = [["string", "string"], ["string", "string"]];

for(var i = 0; i < cubes.length; i++) {
    for(var j = 0; j < cubes[i].length; j++) {
        console.log(cubes[i][j]);
    }
}

他问道:“我能用JavaScript做到这个吗?” - binarious
你在我的评论之后编辑了你的答案。你原来的帖子没有关于迭代的内容。 - Matthew Flaschen

18

虽然有些晚了,但这个解决方案很好而且整洁。

const arr = [[1,2,3],[4,5,6],[7,8,9,10]]
for (let i of arr) {
  for (let j of i) {
    console.log(j) //Should log numbers from 1 to 10
  }
}

或者在你的情况下:

const arr = [[1,2,3],[4,5,6],[7,8,9]]
for (let [d1, d2, d3] of arr) {
  console.log(`${d1}, ${d2}, ${d3}`) //Should return numbers from 1 to 9
}

注意: 使用ES5 JavaScript编译器(如Babel)时才使用for ... of循环,因为for ... of循环在ES6中已经标准化。

另外一点需要注意: 还有其他的选择,但它们具有一些微妙的差异和行为,例如forEach(), for...in, for...of 和传统的for()循环。根据您的情况来决定使用哪个。(ES6还具有.map(), .filter(), .find(), .reduce()


有一个错误:standardised in ES6, so only use this if you have an ES5,应该是 ES6。此外,在 another note 中你再次提到了 for ... of - Timo

8

一个高效的遍历数组的方法是使用内置数组方法 .map()

对于一维数组,代码如下:

function HandleOneElement( Cuby ) {
   Cuby.dimension
   Cuby.position_x
   ...
}
cubes.map(HandleOneElement) ; // the map function will pass each element

对于二维数组:

cubes.map( function( cubeRow ) { cubeRow.map( HandleOneElement ) } )

任何形式的n维数组:
Function.prototype.ArrayFunction = function(param) {
  if (param instanceof Array) {
    return param.map( Function.prototype.ArrayFunction, this ) ;
  }
  else return (this)(param) ;
}
HandleOneElement.ArrayFunction(cubes) ;

5

试试这个:

var i, j;

for (i = 0; i < cubes.length; i++) {
    for (j = 0; j < cubes[i].length; j++) {
       do whatever with cubes[i][j];
    }
}

那将会创建全局变量。 - Matthew Flaschen
5
添加了变量 i、j;公平地说,他没有说:“不要创建全局变量”。;-) - Michael Rice
1
为什么要在新行上声明i和j,而不是在循环中声明?for(var i = 0; ...) - WebDeg Brian

4
或者,您可以使用“forEach()”进行替代:
var cubes = [
 [1, 2, 3],
 [4, 5, 6],    
 [7, 8, 9],
];

cubes.forEach(function each(item) {
  if (Array.isArray(item)) {
    // If is array, continue repeat loop
    item.forEach(each);
  } else {
    console.log(item);
  }
});

如果您需要数组的索引,请尝试使用以下代码:

var i = 0; j = 0;

cubes.forEach(function each(item) {
  if (Array.isArray(item)) {
    // If is array, continue repeat loop
    item.forEach(each);
    i++;
    j = 0;
  } else {
    console.log("[" + i + "][" + j + "] = " + item);
    j++;
  }
});

结果将会如下所示:
[0][0] = 1
[0][1] = 2
[0][2] = 3
[1][0] = 4
[1][1] = 5
[1][2] = 6
[2][0] = 7
[2][1] = 8
[2][2] = 9

2
如果您正在使用ES2015,并且想要定义一个像2-D数组一样迭代的自己的对象,可以通过实现迭代器协议来完成:
  1. 定义一个名为Symbol.iterator@@iterator函数,它返回...
  2. ...一个具有next()函数的对象,该函数返回...
  3. ...一个具有一个或两个属性的对象:一个可选的包含下一个值(如果有)的value和一个布尔值done,如果我们完成了迭代,则为true。
一个一维数组迭代器函数看起来像这样:
// our custom Cubes object which implements the iterable protocol
function Cubes() {
    this.cubes = [1, 2, 3, 4];
    this.numVals = this.cubes.length;

    // assign a function to the property Symbol.iterator
    // which is a special property that the spread operator
    // and for..of construct both search for
    this[Symbol.iterator] = function () { // can't take args

        var index = -1; // keep an internal count of our index
        var self = this; // access vars/methods in object scope

        // the @@iterator method must return an object
        // with a "next()" property, which will be called
        // implicitly to get the next value
        return {
            // next() must return an object with a "done" 
            // (and optionally also a "value") property
            next: function() {
                index++;
                // if there's still some values, return next one
                if (index < self.numVals) {
                    return {
                        value: self.cubes[index],
                        done: false
                    };
                }
                // else there's no more values left, so we're done
                // IF YOU FORGET THIS YOU WILL LOOP FOREVER!
                return {done: true}
            }
        };
    };
}

现在,我们可以将我们的 Cubes 对象视为可迭代对象:
var cube = new Cubes(); // construct our cube object

// both call Symbol.iterator function implicitly:
console.log([...cube]); // spread operator
for (var value of cube) { // for..of construct
    console.log(value);
}

创建我们自己的 2-D 可迭代对象,可以在我们的 next() 函数中返回另一个可迭代对象,而不是返回值:
function Cubes() {
    this.cubes = [
        [1, 2, 3, 4],
        [5, 6, 7, 8],
        [9, 10, 11, 12],
    ];
    this.numRows = this.cubes.length;
    this.numCols = this.cubes[0].length; // assumes all rows have same length

    this[Symbol.iterator] = function () {
        var row = -1;
        var self = this;

        // create a closure that returns an iterator
        // on the captured row index
        function createColIterator(currentRow) {
            var col = -1;
            var colIterator = {}
            // column iterator implements iterable protocol
            colIterator[Symbol.iterator] = function() {
                return {next: function() {
                    col++;
                    if (col < self.numCols) {
                        // return raw value
                        return {
                            value: self.cubes[currentRow][col],
                            done: false
                        };
                    }
                    return {done: true};
                }};
            }
            return colIterator;
        }

        return {next: function() {
            row++;
            if (row < self.numRows) {
                // instead of a value, return another iterator
                return {
                    value: createColIterator(row),
                    done: false
                };
            }
            return {done: true}
        }};
    };
}

现在,我们可以使用嵌套迭代:
var cube = new Cubes();

// spread operator returns list of iterators, 
// each of which can be spread to get values
var rows = [...cube];
console.log([...rows[0]]);
console.log([...rows[1]]);
console.log([...rows[2]]);

// use map to apply spread operator to each iterable
console.log([...cube].map(function(iterator) { 
    return [...iterator];
}));

for (var row of cube) {
    for (var value of row) {
        console.log(value);
    }
}

请注意,我们的自定义可迭代对象并不在所有情况下都像二维数组一样;例如,我们没有实现map()函数。这个答案展示了如何实现一个生成器映射函数(请看此处了解迭代器和生成器之间的差异;此外,生成器是ES2016功能,而不是ES2015,因此如果您使用babel编译,需要更改您的babel预设)。

1

JavaScript没有这样的声明。应该是:

var cubes = ...

无论如何

但你可以做:

for(var i = 0; i < cubes.length; i++)
{
  for(var j = 0; j < cubes[i].length; j++)
  {

  }
}

请注意,JavaScript 允许不规则数组,例如:
[
  [1, 2, 3],
  [1, 2, 3, 4]
]

由于数组可以包含任何类型的对象,包括任意长度的数组。

正如MDC所指出的:

"for..in不应用于迭代重要索引顺序的数组"

如果您使用原始语法,则不能保证以数字顺序访问元素。


1

使用forEach()的ES6变体

基于icyrock.com的答案。

const cubes = [
   [1, 2, 3],
   [4, 5, 6],    
   [7, 8, 9]
];

cubes.forEach((n, i) => {
    n.forEach((b, j) => {
        console.log(`cubes[${i}][${j}] = ${b}`);
    });
});


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接