JavaScript遍历稀疏数组

13
我有一个稀疏数组(索引不连续),像这样:
var testArray = { 0: "value1", 5: "value2", 10: "value3", 15: "value4" };

我只想遍历每个项目,做一些操作,并能在特定条件下中断。
我对JavaScript还比较新手,没有找到合适的方法。这是我尝试过的方法:
  1. 内置的 "for..in"。似乎这不是迭代数组的正确方式

  2. 来自 ECMASCRIPT5 的 forEach。这个可以正确迭代,但我无法从循环中跳出。

  3. 来自 Underscore.js 的 _.each()。与 #2 相同结果。

  4. 来自 JQuery 的 $.each()。使用此方法,我可以通过返回 false 跳出循环,但它无法正确迭代。对于上面的示例,它将在 0、5、10、15 处迭代,而不是我期望的位置。

所以我的问题是:在Javascript中是否有一种简单的方法可以迭代稀疏数组,并且在循环期间可能中断循环,还是最好使用另一种数据结构,如哈希表?如果是这样,有什么推荐吗?
谢谢!

3
如果您查看第1点中提供的链接,内置的for..in循环就是遍历对象属性的方法,这也是您所拥有的。 - anson
1
从我理解的链接中得知,如果一个库向Array.prototype添加成员,则可能会出现问题。然后该成员将成为“for..in”循环的一部分。 - Absolom
1
啊,好的,我的例子无效,因为我使用的是对象而不是数组。所以你的评论是正确的。 - Absolom
1
另一个SO页面说:“如果你的代码中的其他地方存在Array.Prototype.foo = 1,那么你将会被咬在屁股上。” 这里不适用吗? - john k
5个回答

10
< p > for...in 语法有什么问题呢?你有一个对象,因此使用for...in 语法完全是有效的:


var testArray = { 0: "value1", 5: "value2", 10: "value3", 15: "value4" };

for (var key in testArray) {
  var value = testArray[key];

  if (...) {
    break;
  }
}

1
谢谢你的回答。但是如果我想使用真正的数组而不是对象呢?那么你会用什么? - Absolom
1
@Absolom JS数组继承自Object,所以这仍然可以正常工作。 - mikemaccana
5
for..in循环会保证顺序吗? - Scott Boring
1
@Blender:如果您想按顺序迭代,您会如何处理?我在Javascript中缺少一个TreeMap。 - Janus Troelsen
1
@JanusTroelsen:在创建对象时,顺序不会被保留,所以你无法做到。 - Blender
@ScottBoring 是的,它确实有:根据现代 ECMAScript 规范,遍历顺序是明确定义的,并且在所有实现中保持一致。在原型链的每个组件中,所有非负整数键(即可以作为数组索引的键)将按值的升序先遍历,然后按属性创建的时间顺序升序遍历其他字符串键。详见:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in - undefined

3

你所拥有的不是数组,而只是一个对象。

您可以通过以下方式进行测试:

Array.isArray(testArray)

值得一提的是,JavaScript确实有被称为稀疏数组的数组。当您使用delete运算符删除一个元素或将长度属性更改为较大值时,就会发生这种情况。
回答您的问题,遍历对象的最佳方式是Object.keys(obj).forEach()
var o = {"a":3, "b":4};

Object.keys(o).forEach(
    function (key) {
        var val = o[key];
        console.log("Key:" + key);
        console.log("Value:" + val);
    }
);
< p >使用for(var p in o){...}的可能问题是它也会遍历父级中可枚举的属性(即原型链)。通常情况下不会发生这种情况,如果您使用字面表达式定义对象var obj = {...},则默认其父级为Object.prototype,并且它没有任何可枚举的属性。


3

当处理对象哈希表时,使用 for..in 并不是最糟糕的事情。但是在处理数组 ([]) 时应该避免使用它,但在这里使用应该没问题:

var val;
for (index in testArray) {
  if (index == 10) {
    break;
  } else {
    val = testArray[index];
  }
}

2

首先你需要放弃的是“Array”这个概念。在ECMAscript中并没有真正的Array(不考虑类型数组和二进制技巧)。

所以,你手头上拥有的只是一个普通的Object。如果你想要遍历它,我建议你使用.forEach,如果你支持ES5的话。如果你需要在迭代过程中提前退出,你可能需要使用ES5方法,如.some().every(),例如:

Object.keys( testArray ).some(function( key ) {
    if( +key < 15 ) {            
        return true;
    }

    console.log(key, testArray[key]);
});

当遇到一个数值不小于15的键时,通过返回true来中断迭代。


1
你也可以使用(滥用)Array.every代替Array.fromEach来提前退出循环。

var pre = document.getElementById('console');

var arr = new Array();
arr[10] = 'Hello';
arr[20] = 'World!';
arr[30] = 'stop';
arr[40] = 'Goodbye';

arr.every(function (val, idx) {
  if (val !== 'stop') {
    pre.textContent += idx+': '+val+'\n';
    return true;
  }
});
<pre id="console"></pre>

基本上,Array.every 一旦其中一个元素返回 false,就立即返回 false。这使你可以提前跳出循环。

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