假设我有以下内容:

var array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]

获得所有不同年龄的数组的最佳方法是什么,这样我就得到了一个结果数组:

[17, 35]

是否有一些方法,我可以选择结构数据或更好的方法,这样我就不必遍历每个数组检查“年龄”的值,并检查另一个数组是否存在,如果没有添加它?

如果有某种方法可以让我不用迭代就能得到不同的年龄……

目前效率低下的方式,我想改进…如果它的意思不是“数组”是一个对象的数组,而是一个对象的“映射”与一些唯一的键(即。"1,2,3")也可以。我只是在寻找最高效的方式。

以下是我目前的做法,但对我来说,迭代似乎只是为了提高效率,即使它确实有效……

var distinct = []
for (var i = 0; i < array.length; i++)
   if (array[i].age not in distinct)
      distinct.push(array[i].age)

当前回答

使用d3.js v3:

  ages = d3.set(
    array.map(function (d) { return d.age; })
  ).values();

其他回答

您可以使用像这样的字典方法。基本上,你在“字典”中指定你想要不同的值作为键(这里我们使用数组作为对象以避免使用字典模式)。如果该键不存在,则将该值添加为distinct。

下面是一个工作演示:

数组var =[{“名称”:“乔”,“年龄”:17},{“名称”:“鲍勃”,“年龄”:17},{“名称”:“卡尔”,“年龄”:35}); Var unique = []; Var distinct = []; 对于(设I = 0;I < array.length;我+ +){ 如果(!独特[array[我].age]) { distinct.push(数组[我].age); 独特的[[我]数组。年龄:1岁; } } var d = document.getElementById("d"); d.innerHTML = "" + distinct; < div id = " d " > < / div >

这将是O(n)其中n是数组中对象的数量m是唯一值的数量。没有比O(n)更快的方法了,因为每个值必须至少检查一次。

以前的版本使用对象,for in。这些都是小的性质,并已在上面进行了小的更新。然而,原始jsperf的两个版本之间的性能似乎有所提高的原因是由于数据样本量太小。因此,前一个版本中的主要比较是查看内部映射和过滤器使用与字典模式查找之间的差异。

如上所述,我更新了上面的代码,但是,我也更新了jsperf以查找1000个对象而不是3个对象。3忽略了许多涉及的性能缺陷(过时的jsperf)。

性能

https://jsperf.com/filter-vs-dictionary-more-data当我运行这个字典是96%快。

目前正在使用typescript库以orm方式查询js对象。你可以从下面的链接下载。这个答案解释了如何使用下面的库来解决。

https://www.npmjs.com/package/@krishnadaspc/jsonquery?activeTab=readme

var ageArray = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]

const ageArrayObj = new JSONQuery(ageArray)
console.log(ageArrayObj.distinct("age").get()) // outputs: [ { name: 'Bob', age: 17 }, { name: 'Carl', age: 35 } ]

console.log(ageArrayObj.distinct("age").fetchOnly("age")) // outputs: [ 17, 35 ]

Runkit live链接:https://runkit.com/pckrishnadas88/639b5b3f8ef36f0008b17512

刚找到这个,我觉得很有用

_.map(_.indexBy(records, '_id'), function(obj){return obj})

还是用下划线,如果你有一个这样的对象

var records = [{_id:1,name:'one', _id:2,name:'two', _id:1,name:'one'}]

它只会给你唯一的对象。

这里发生的是indexBy返回一个像这样的映射

{ 1:{_id:1,name:'one'}, 2:{_id:2,name:'two'} }

因为它是一个映射,所有的键都是唯一的。

然后我把这个列表映射回数组。

如果你只需要不同的值

_.map(_.indexBy(records, '_id'), function(obj,key){return key})

请记住,键是作为字符串返回的,因此,如果您需要整数,您应该这样做

_.map(_.indexBy(records, '_id'), function(obj,key){return parseInt(key)})

使用新的Ecma功能很棒,但并不是所有用户都有这些功能。

下面的代码将附加一个名为distinct的新函数到Global Array对象。 如果您试图获取对象数组的不同值,则可以传递值的名称以获取该类型的不同值。

Array.prototype.distinct = function(item){   var results = [];
for (var i = 0, l = this.length; i < l; i++)
    if (!item){
        if (results.indexOf(this[i]) === -1)
            results.push(this[i]);
        } else {
        if (results.indexOf(this[i][item]) === -1)
            results.push(this[i][item]);
    }
return results;};

请查看我在CodePen中发布的演示。

这里有另一种解决方法:

var result = {};
for(var i in array) {
    result[array[i].age] = null;
}

result = Object.keys(result);

or

result = Object.values(result);

我不知道这个解决方案与其他解决方案相比有多快,但我喜欢更干净的外观。:-)


编辑:好吧,上面的似乎是最慢的解决方案。

我在这里创建了一个性能测试用例:http://jsperf.com/distinct-values-from-array

我没有测试年龄(整数),而是选择比较名称(字符串)。

方法1 (TS的解决方案)非常快。有趣的是,Method 7比所有其他解决方案都好,这里我只是摆脱了.indexOf(),并使用了它的“手动”实现,避免了循环函数调用:

var result = [];
loop1: for (var i = 0; i < array.length; i++) {
    var name = array[i].name;
    for (var i2 = 0; i2 < result.length; i2++) {
        if (result[i2] == name) {
            continue loop1;
        }
    }
    result.push(name);
}

使用Safari和Firefox在性能上的差异是惊人的,似乎Chrome在优化方面做得最好。

我不太确定为什么上面的片段比其他片段快,也许比我更聪明的人有答案。:-)