如何在Javascript中获取数组键?


80

我用以下代码创建了一个数组:

var widthRange = new Array();
widthRange[46] = { min:0,  max:52 };
widthRange[66] = { min:52, max:70 };
widthRange[90] = { min:70, max:94 };

我想循环获取每个值46、66、90。我尝试过,for (var key in widthRange)但这给了我很多额外的属性(我假设它们是对象上的函数)。我不能使用常规的for循环,因为这些值不是连续的。


9
看起来您所拥有的数据虽然恰好具有数字键,但实际上并不是数组数据。我将在这里使用常规对象。
昆汀

@Quentin称为稀疏数组。在性能方面,最好在这里使用数组而不是对象。此外,就性能而言,最佳答案甚至没有列出:Array.prototype.forEach。调用Object.keys数组的效果不佳,因为浏览器不会对此进行优化。for(var key in array)这很不好,因为它遍历了原型并对其遇到的每个数字键进行了字符串化(将double转换为base10非常慢)。forEach然而,正是专为迭代稀疏数组的目的,相对于其他的解决方案将会给你的代码性能优良
杰克·吉芬

Answers:


96

您需要调用该hasOwnProperty函数来检查属性是否实际上是在对象本身上定义的(相对于其原型),如下所示:

for (var key in widthRange) {
    if (key === 'length' || !widthRange.hasOwnProperty(key)) continue;
    var value = widthRange[key];
}

请注意,您需要单独检查length
但是,您根本不应该在这里使用数组。您应该使用常规对象。所有Javascript对象均用作关联数组。

例如:

var widthRange = { };  //Or new Object()
widthRange[46] = { sel:46, min:0,  max:52 };
widthRange[66] = { sel:66, min:52, max:70 };
widthRange[90] = { sel:90, min:70, max:94 };

2
真?哇,我每天在这里都学到一些东西,而且常常是从SLaks那里获得的:-)我很惊讶Array跟踪这样的显式设置的索引。也许我不应该。
Pointy

@SLaks:谢谢,将其更改为对象似乎是最好的解决方案。还有一个问题:是否有一种以相反顺序进行迭代的方法?
DisgruntledGoat 2010年

没有; 您需要自己做。
SLaks

2
为什么需要单独检查length?并非[[DontEnum]]在所有浏览器中都标记为吗?
Roatin Marth'3

@Roatin:我不知道。安全胜过遗憾。
Slaks


18

如果您要进行任何类型的数组/集合操作或检查,我强烈建议您使用Underscore.js。它很小,经过了充分的测试,可以为您节省几天/几周/几年的JavaScript头痛。这是它的按键功能:

按键

检索对象属性的所有名称。

_.keys({one : 1, two : 2, three : 3});
=> ["one", "two", "three"]

4

说你的数组看起来像 arr = [ { a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }, { a: 7, b: 8, c: 9 } ](或其他键),你可以做

arr.map((o) => {
    return Object.keys(o)
}).reduce((prev, curr) => {
    return prev.concat(curr)
}).filter((col, i, array) => {
    return array.indexOf(col) === i
});

["a", "b", "c"]


3
for (var i = 0; i < widthRange.length; ++i) {
  if (widthRange[i] != null) {
    // do something
  }
}

您不能真正获得设置好的键,因为这不是数组的工作原理。设置元素46后,也将设置0到45(尽管它们为空)。

您总是可以有两个数组:

var widthRange = [], widths = [], newVal = function(n) {
  widths.push(n);
  return n;
};
widthRange[newVal(26)] = { whatever: "hello there" };

for (var i = 0; i < widths.length; ++i) {
  doSomething(widthRange[widths[i]]);
}

编辑好可能是我在这里湿了...


数组仅跟踪长度,不会遍历并创建许多对象。基本上,当找到新索引时,它会检查该新索引是否大于长度...如果是,则新索引变为新长度。因此,只能获取您使用for-in循环定义的索引
Bob


1

您的原始示例对我来说效果很好:

<html>
<head>
</head>
<body>
<script>
var widthRange = new Array();
widthRange[46] = { sel:46, min:0,  max:52 };
widthRange[66] = { sel:66, min:52, max:70 };
widthRange[90] = { sel:90, min:70, max:94 };

var i = 1;
for (var key in widthRange)
{
    document.write("Key #" + i + " = " + key + "; &nbsp;&nbsp;&nbsp; min/max = " + widthRange[key].min + "/" + widthRange[key].max + "<br />");
    i++;
}
</script>
</html>

浏览器中的结果(在Windows XP上为Firefox 3.6.2):

Key #1 = 46;     min/max = 0/52
Key #2 = 66;     min/max = 52/70
Key #3 = 90;     min/max = 70/94

2
我敢打赌他正在导入Prototype或类似的东西。通常,那些“ in”循环是有风险的。
Pointy

好吧,我使用的是jQuery,但也使用的是使用mootools的Joomla,有些东西正在添加这些属性。
DisgruntledGoat

1

我认为您应该为此使用Object({})而不是array([])。

一组数据与每个键关联。它为使用对象而尖叫。做:

var obj = {};
obj[46] = { sel:46, min:0,  max:52 };
obj[666] = { whatever:true };

// This is what for..in is for
for (var prop in obj) {
  console.log(obj[prop]);
}

也许有些实用工具可以帮助您:

window.WidthRange = (function () {
  var obj = {};
  return {
    getObj: function () {return obj;}
    , add: function (key, data) {
        obj[key] = data;
        return this; // enabling chaining
      }
  }
})();

// Usage (using chaining calls):
WidthRange.add(66, {foo: true})
.add(67, {bar: false})
.add(69, {baz: 'maybe', bork:'absolutely'});

var obj = WidthRange.getObj();
for (var prop in obj) {
  console.log(obj[prop]);
}

0

似乎可以工作。

var widthRange = new Array();
widthRange[46] = { sel:46, min:0,  max:52 };
widthRange[66] = { sel:66, min:52, max:70 };
widthRange[90] = { sel:90, min:70, max:94 };

for (var key in widthRange)
{
    document.write(widthRange[key].sel + "<br />");
    document.write(widthRange[key].min + "<br />");
    document.write(widthRange[key].max + "<br />");
}

0

我写了一个函数,该函数对Objects的每个实例都适用(数组就是这些实例)。

Object.prototype.toArray = function()
{
    if(!this)
    {
      return null;
    }

    var c = [];

    for (var key in this) 
    {
        if ( ( this instanceof Array && this.constructor === Array && key === 'length' ) || !this.hasOwnProperty(key) ) 
        {
            continue;
        }

        c.push(this[key]);
    }

    return c;
};

用法:

var a   = [ 1, 2, 3 ];
a[11]   = 4;
a["js"] = 5;

console.log(a.toArray());

var b = { one: 1, two: 2, three: 3, f: function() { return 4; }, five: 5 };
b[7] = 7;

console.log(b.toArray());

输出:

> [ 1, 2, 3, 4, 5 ]
> [ 7, 1, 2, 3, function () { return 4; }, 5 ]

它对任何人都可能有用。


2
扩展未创建的原型是一个非常糟糕的主意,无论您认为它有多方便。
doug65536

0

... ????

或者,如果您有要使用的物品清单...

var range = [46, 66, 90]
    , widthRange=[]
    , write=[];

    widthRange[46] = { min:0, max:52 }; 
    widthRange[66] = { min:52, max:70 }; 
    widthRange[90] = { min:70, max:94 }; 

for(var x=0; x<range.length; x++){var key, wr;

    key = range[x];

    wr = widthRange[key] || false;

    if(wr===false){continue;}

    write.push(['key: #',key, ', min: ', wr.min, 'max:', wr.max].join(''));

    }

0

对于您的输入数据:

let widthRange = new Array()
widthRange[46] = { min:0,  max:52 }
widthRange[61] = { min:52, max:70 }
widthRange[62] = { min:52, max:70 }
widthRange[63] = { min:52, max:70 }
widthRange[66] = { min:52, max:70 }
widthRange[90] = { min:70, max:94 }

声明式方法:

const relevantKeys = [46,66,90]
const relevantValues = Object.keys(widthRange)
    .filter(index => relevantKeys.includes(parseInt(index)))
    .map(relevantIndex => widthRange[relevantIndex])

Object.keys获取密钥,parseInt用于将它们转换为数字。 filter只得到你想要的。 mapObject.keys丢失的对象值开始,仅从您要查找的索引的原始对象构建数组。

调试:

console.log(widthRange)
console.log(relevantKeys)
console.log(relevantValues)

0

这个问题已经很老了,但是现在您可以使用forEach了,它非常有效,并且会将键保留为数字:

let keys = widthRange.map((v,k) => k).filter(i=>i!==undefined))

这将遍历widthRange并使用键的值创建一个新数组,然后仅采用定义的值来滤除所有稀疏的插槽。

(这是个好主意,但出于节俭的考虑:如果插槽0始终为空,则可以将其缩短为filter(i=>i)filter(Boolean)

而且,它的效率可能较低,但是数字可以用 let keys = Object.keys(array).map(i=>i*1)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.