如何从JavaScript中的对象数组中获取不同的值?

How to get distinct values from an array of objects in JavaScript?

我有一个assuming以下:

1
2
3
4
5
6
var array =
    [
        {"name":"Joe","age":17},
        {"name":"Bob","age":17},
        {"name":"Carl","age": 35}
    ]

什么是最好的方式能够获得一个数组的不同的行为的所有我得到这样的结果:一个数组。

1
[17, 35]

有一些方式可以替代的数据结构(或更好的方法,这样我就不会要iterate通每个阵列检测的价值和检查对一个"时代"的生存和增加其阵,如果它不是吗?

如果有人能在一路就没有明显的年龄iterating拔……

我想获取电流的方式提高了……如果它意味着"阵列"而被一个数组对象的"地图",但具有一些独特的重点对象(即"1"),这将是太好了。我只是在寻找最高效节能的方式。

以下是我目前的它,但在我看来,它只是被糟糕的迭代效率虽然它做的工作……

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


如果您使用ES6/ES2015或更高版本,您可以这样做:

1
const unique = [...new Set(array.map(item => item.age))];

下面是一个如何做到这一点的例子。


你可以使用像这样的字典方法。基本上,您将要区分的值指定为字典中的键。如果键不存在,则将该值作为distinct添加。

1
2
3
4
5
6
7
8
var unique = {};
var distinct = [];
for( var i in array ){
 if( typeof(unique[array[i].age]) =="undefined"){
  distinct.push(array[i].age);
 }
 unique[array[i].age] = 0;
}

下面是一个工作演示:http://jsfiddle.net/jbukp/1

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

性能

当我运行这个字典时,http://jspef.com/filter-versus-dictionary快了30%。


如果这是PHP,我会用键构建一个数组,并在末尾使用array_keys,但是JS没有这样的奢华。相反,请尝试以下操作:

1
2
3
4
5
6
var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
    if( flags[array[i].age]) continue;
    flags[array[i].age] = true;
    output.push(array[i].age);
}


使用ES6

1
2
3
4
5
6
7
8
9
let array = [
  {"name":"Joe","age": 17 },
  {"name":"Bob","age": 17 },
  {"name":"Carl","age": 35 }
];
array.map(item => item.age)
  .filter((value, index, self) => self.indexOf(value) === index)

> [17, 35]


从2017年8月25日起,您可以使用ES6的新字体集来解决这个问题。

1
Array.from(new Set(yourArray.map((item: any) => item.id)))


使用ES6功能,您可以执行以下操作:

1
const uniqueAges = [...new Set( array.map(obj => obj.age)) ];

我只需要映射并删除DUP:

1
2
3
4
var ages = array.map(function(obj) { return obj.age; });
ages = ages.filter(function(v,i) { return ages.indexOf(v) == i; });

console.log(ages); //=> [17, 35]

编辑:哎呀!在性能方面不是最有效的方法,而是最简单易读的IMO。如果你真的关心微观优化或者你有大量的数据,那么一个规则的for循环将更加"有效"。


@travis-j答案的forEach版本(对现代浏览器和节点j s world有帮助):

1
2
3
4
5
6
7
8
var unique = {};
var distinct = [];
array.forEach(function (x) {
  if (!unique[x.age]) {
    distinct.push(x.age);
    unique[x.age] = true;
  }
});

Chrome V29.0.1547快34%:http://jspef.com/filter-vers-dictionary/3

以及采用映射器函数的通用解决方案(比直接映射慢一点,但这是预期的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function uniqueBy(arr, fn) {
  var unique = {};
  var distinct = [];
  arr.forEach(function (x) {
    var key = fn(x);
    if (!unique[key]) {
      distinct.push(key);
      unique[key] = true;
    }
  });
  return distinct;
}

// usage
uniqueBy(array, function(x){return x.age;}); // outputs [17, 35]


默认情况下,我已经开始在所有新项目中添加下划线,这样我就不必考虑这些小数据挖掘问题。

1
2
var array = [{"name":"Joe","age":17}, {"name":"Bob","age":17}, {"name":"Carl","age": 35}];
console.log(_.chain(array).map(function(item) { return item.age }).uniq().value());

生成[17, 35]


使用土灰

1
2
3
4
5
6
7
var array = [
    {"name":"Joe","age": 17 },
    {"name":"Bob","age": 17 },
    {"name":"Carl","age": 35 }
];
_.chain(array).pluck('age').unique().value();
> [17, 35]


There are many valid answers already, but I wanted to add one that uses only the reduce() method because it is clean and simple.

1
2
3
4
5
6
function uniqueBy(arr, prop){
  return arr.reduce((a, d) => {
    if (!a.includes(d[prop])) { a.push(d[prop]); }
    return a;
  }, []);
}

这样使用:

1
2
3
4
5
6
7
8
var array = [
  {"name":"Joe","age": 17},
  {"name":"Bob","age": 17},
  {"name":"Carl","age": 35}
];

var ages = uniqueBy(array,"age");
console.log(ages); // [17, 35]

另一种解决方法是:

1
2
3
4
5
var result = {};
for(var i in array) {
    result[array[i].age] = null;
}
result = Object.keys(result);

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

编辑:好的,以上似乎是所有解决方案中最慢的。

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

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

方法1(TS的解决方案)非常快。有趣的是,方法7的性能优于所有其他解决方案,这里我去掉了.indexof()并使用了它的"手动"实现,避免了循环函数调用:

1
2
3
4
5
6
7
8
9
10
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在优化方面做得最好。

我不太清楚为什么上面的片段比其他片段快,也许比我聪明的人有答案。;-)


1
2
3
4
5
6
7
8
9
10
11
function get_unique_values_from_array_object(array,property){
    var unique = {};
    var distinct = [];
    for( var i in array ){
       if( typeof(unique[array[i][property]]) =="undefined"){
          distinct.push(array[i]);
       }
       unique[array[i][property]] = 0;
    }
    return distinct;
}


不足之处_.uniq(_.pluck(array,"age"))


使用土灰

1
2
3
4
5
6
7
var array = [
    {"name":"Joe","age": 17 },
    {"name":"Bob","age": 17 },
    {"name":"Carl","age": 35 }
];

_.chain(array).map('age').unique().value();

返回[17,35]


我想您正在寻找groupby函数(使用lodash)

1
2
3
4
5
_personsList = [{"name":"Joe","age":17},
                {"name":"Bob","age":17},
                {"name":"Carl","age": 35}];
_uniqAgeList = _.groupBy(_personsList,"age");
_uniqAges = Object.keys(_uniqAgeList);

产生结果:

1
17,35

jsfiddle演示:http://jsfiddle.net/4j2sx/201/


1
2
3
4
5
6
7
8
var unique = array
    .map(p => p.age)
    .filter((age, index, arr) => arr.indexOf(age) == index)
    .sort(); // sorting is optional

// or in ES6

var unique = [...new Set(array.map(p => p.age))];


如果您有array.prototype.include或愿意对其进行polyfill,则可以:

1
var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.push(x.age); });

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

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

再次使用下划线,因此如果您有这样的对象

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

它只会给你唯一的对象。

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

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

因为它是一张地图,所以所有的键都是独一无二的。

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

如果只需要不同的值

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

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

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


如果像我一样,您喜欢更"实用"而不影响速度,那么这个示例使用包装在reduce闭包中的快速字典查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var array =
[
    {"name":"Joe","age":17},
    {"name":"Bob","age":17},
    {"name":"Carl","age": 35}
]
var uniqueAges = array.reduce((p,c,i,a) => {
    if(!p[0][c.age]) {
        p[1].push(p[0][c.age] = c.age);
    }
    if(i<a.length-1) {
        return p
    } else {
        return p[1]
    }
}, [{},[]])

根据这个测试,我的解决方案比建议的答案快两倍。


下面的代码将显示唯一的年龄数组以及没有重复年龄的新数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var data = [
  {"name":"Joe","age": 17},
  {"name":"Bob","age": 17},
  {"name":"Carl","age": 35}
];

var unique = [];
var tempArr = [];
data.forEach((value, index) => {
    if (unique.indexOf(value.age) === -1) {
        unique.push(value.age);
    } else {
        tempArr.push(index);    
    }
});
tempArr.reverse();
tempArr.forEach(ele => {
    data.splice(ele, 1);
});
console.log('Unique Ages', unique);
console.log('Unique Array', data);```

这里有一个通用的解决方案,它使用reduce,允许映射,并保持插入顺序。

项目:数组

映射器:将项映射到条件的一元函数,或者为空以映射项本身。

1
2
3
4
5
6
7
function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        if (acc.indexOf(item) === -1) acc.push(item);
        return acc;
    }, []);
}

用法

1
2
const distinctLastNames = distinct(items, (item)=>item.lastName);
const distinctItems = distinct(items);

您可以将此添加到数组原型中,如果这是您的样式,则省去items参数…

1
2
const distinctLastNames = items.distinct( (item)=>item.lastName) ) ;
const distinctItems = items.distinct() ;

您还可以使用集合而不是数组来加速匹配。

1
2
3
4
5
6
7
function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        acc.add(item);
        return acc;
    }, new Set());
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unique(obj, prop) {
    let result = [];
    let seen = new Set();

    Object.keys(obj)
        .forEach((key) => {
            let value = obj[key];

            let test = !prop
                ? value
                : value[prop];

            !seen.has(test)
                && seen.add(test)
                && result.push(value);
        });

    return result;
}

使用新的ECMA功能非常好,但并非所有用户都有这些功能。

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

1
2
3
4
5
6
7
8
9
10
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中的帖子以获取演示。


试一试

1
2
3
4
5
6
7
8
9
var x = [] ;
for (var i = 0 ; i < array.length ; i++)
{
 if(x.indexOf(array[i]['age']) == -1)
  {
    x.push(array[i]['age']);
  }
}
console.log(x);

简单的单内衬,性能卓越。比我测试中的ES6解决方案快6%。

var ages=array.map(函数(o)返回o.age).filter(函数(v,i,a)返回a.indexof(v)==i);


我在这个功能上的两分钱:

1
2
3
4
5
6
var result = [];
for (var len = array.length, i = 0; i < len; ++i) {
  var age = array[i].age;
  if (result.indexOf(age) > -1) continue;
  result.push(age);
}

您可以在这里看到结果(方法8)http://jspef.com/distinct-values-from-array/3


此函数可以唯一的数组和对象

1
2
3
4
5
6
7
8
9
10
11
12
function oaunic(x,n=0){
    if(n==0) n ="elem";
    else n ="elem."+n;
    var uval = [];
    var unic = x.filter(function(elem, index, self){
        if(uval.indexOf(eval(n)) < 0){
            uval.push(eval(n));
            return index == self.indexOf(elem);
        }
    })
    return unic;
}

像这样用

1
2
3
4
tags_obj = [{name:"milad
<hr><P>使用D3.JS V3:</P>[cc lang="
javascript"]  ages = d3.set(
    array.map(function (d) { return d.age; })
  ).values();