假设我具有以下内容:

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


能够获得所有不同年龄的数组的最佳方法是什么,这样我得到以下结果数组:

[17, 35]


是否有某种方法可以替代地构造数据或更好的方法,从而使我不必遍历每个数组来检查“ age”的值并检查

如果有某种方法,我可以重复使用不同的年龄而无需重复...

当前无效的方法我想改进...如果这意味着不是“数组”是对象数组,而是带有一些唯一键(即“ 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)


评论

迭代并不是“效率低下”,并且您不能“不迭代”地对每个元素做任何事情。您可以使用各种具有功能外观的方法,但最终,必须在某种程度上对这些项目进行迭代。

// 100%正在运行的代码const listOfTags = [{{id:1,标签:“ Hello”,颜色:“ red”,排序:0},{id:2,标签:“ World”,颜色:“ green”,排序:1},{id:3,标签:“ Hello”,颜色:“ blue”,排序:4},{id:4,标签:“ Sunshine”,颜色:“ yellow”,排序:5},{id :5,5,标签:“ Hello”,颜色:“红色”,排序:6}],键= ['标签','颜色'],已过滤= listOfTags.filter((s => o =>(k => !s.has(k)&& s.add(k))(keys.map(k => o [k])。join('|')))(新集合)); console.log(已过滤);

赏金很大,但此处已回答了具有给定数据和答案的问题:stackoverflow.com/questions/53542882/…。赏金的目的是什么?我应该用两个或多个键来回答这个特定问题吗?

设置对象和地图是浪费的。这项工作仅需要一个简单的.reduce()阶段。

#1 楼

如果这是PHP,我会用键构建一个数组,并在最后加上array_keys,但是JS却没有这样的奢侈。而是尝试以下操作:

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);
}


评论


不可以,因为array_unique会比较整个项目,而不仅仅是此处的年龄。

–克服黑暗的绝对
13年2月28日在2:18

我认为标志= {}比标志= []更好

– zhuguowei
17年6月12日在9:35

也许@zhuguowei,尽管稀疏数组没有什么大不了-而且我还假设age是一个相对较小的整数(肯定小于120)

–克服黑暗的绝对
17年6月12日在14:56

我们如何还能打印出具有相同年龄的总人数?

–user1788736
19年3月20日在1:43

#2 楼

如果您使用的是ES6 / ES2015或更高版本,则可以通过以下方式进行操作:
const data = [
  { group: 'A', name: 'SD' }, 
  { group: 'B', name: 'FI' }, 
  { group: 'A', name: 'MM' },
  { group: 'B', name: 'CO'}
];
const unique = [...new Set(data.map(item => item.group))]; // [ 'A', 'B']

下面是一个示例。

评论


我收到一个错误:TypeError :(中间值).slice不是一个函数

– AngJobs在Github上
16年7月13日在10:28

@Thomas ...表示传播算子。在这种情况下,这意味着创建新集并将其散布在列表中。您可以在这里找到有关它的更多信息:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

– Vlad Bezden
17年6月16日在14:09

对于打字稿,您必须使用包装在Array.from()中的新Set ...我将在下面提供该答案。 @AngJobs

–克里斯蒂安·马修(Christian Matthew)
17年8月25日在16:24



是否可以根据对象中的多个键找出唯一的对象?

– Jefree Sujit
17年3月3日,12:24

@Russell Vea几乎在几个月前就给出了该解决方案。您检查现有答案了吗?但是话又说回来,266票以上的投票结果表明,没有人检查过。

– Dan Dascalescu
18-09-28在3:18

#3 楼

使用ES6

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]


评论


如果我想获取带有索引的值,那么如何获取示例:我想获取{“ name”:“ Bob”,“ age”:“ 17”},{“ name”:“ Bob”,“ age “:” 17“}

–阿卜杜拉·阿尔·蒙蒙(Abdullah Al Mamun)
18/09/12在3:51



这就是为什么您必须继续滚动

–亚历杭德罗·巴斯迪达斯(Alejandro Bastidas)
18/09/12在19:42

@AbdullahAlMamun,您可以在.filter中使用map而不是先调用.map,如下所示:array.filter((value,index,self)=> self.map(x => x.age).indexOf(value.age) ==索引)

–狮子座
18/09/14在13:42

ps:.filter内的.map可能很慢,因此使用大型数组时要小心

–狮子座
18/09/14在13:53

@IvanNosov您的代码段非常好,尽管几行解释如何与映射和过滤器文档的指针一起使用的内容对于初学者而言是完美而清晰的

– azakgaim
18年11月20日在16:11

#4 楼

您可以使用像这样的字典方法。基本上,您将要与众不同的值分配为“字典”中的键(此处,我们使用数组作为对象以避免字典模式)。如果键不存在,则将该值添加为不重复。

这是一个有效的演示:




 var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}];
var unique = [];
var distinct = [];
for( let i = 0; i < array.length; i++ ){
  if( !unique[array[i].age]){
    distinct.push(array[i].age);
    unique[array[i].age] = 1;
  }
}
var d = document.getElementById("d");
d.innerHTML = "" + distinct; 

 <div id="d"></div> 





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

以前的版本使用的是对象,并且用于in。这些本质上是次要的,并且自在上面做了些微更新。但是,原始jsperf中两个版本之间的性能似乎有所提高的原因是由于数据样本大小很小。因此,以前版本中的主要比较是查看内部映射和过滤器使用与字典模式查找之间的区别。

如上所述,我已经更新了上面的代码,但是,我也进行了更新更新了jsperf,使其可以查看1000个对象而不是3个对象。3个忽略了许多性能陷阱(过时的jsperf)。

性能

https://jsperf.com/ filter-vs-dictionary-more-data当我运行该字典时,速度提高了96%。



评论


快速,并且不需要任何其他插件。真的很酷

– mihai
15年12月14日在21:41

怎么样if(typeof(unique [array [i] .age])==“ undefined”){unique.push(array [i] .age); unique [array [i] .age] = 0; }

–永恒
16-10-7在4:47



哇,性能确实发生了变化,转而使用了map选项。单击该jsperf链接!

– A T
17年4月2日在15:53

@ 98percentmonkey-该对象用于简化“词典方法”。在对象内部,将跟踪的每个事件都添加为键(或仓)。这样,当我们遇到一个事件时,我们可以检查键(或bin)是否存在。如果确实存在,则我们知道该事件不是唯一的;如果它不存在,则我们知道该事件是唯一的,然后将其添加。

–特拉维斯J
18年7月31日在20:02

@ 98percentmonkey-使用对象的原因是查找为O(1),因为它只检查一次属性名,而数组为O(n),因为它必须查看每个值以检查其存在。在过程结束时,对象中的一组键(仓)代表一组唯一的事件。

–特拉维斯J
18年7月31日在20:02

#5 楼

截至2017年8月25日,这是您使用ES6中用于Typescript的新Set来解决此问题的方法

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


评论


这有所帮助,感谢之前。类型“设置”不是数组类型

–罗伯特·金
18年4月5日在23:38

这是一个很好的答案。直到我向下滚动到这一行,我才写一个答案:Object.keys(values.reduce (((x,y)=> {x [y] = null; return x;},{} ));但是,此答案更为简洁,适用于所有数据类型,而不仅仅是字符串。

– jgosar
19年11月11日在8:28

这个答案是惊人的!

– lukeocodes
5月24日16:17

感谢您帮助我们的打字机!

– Mallenswe
8月6日4:40

#6 楼

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

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


评论


这种方法仅适用于原始类型(在这种情况下,年龄是数字数组),但对于对象将失败。

–k0pernikus
19年6月24日在13:22

任何想法如何使其具有两个键的对象工作?

–cegprakash
19年9月5日在12:40

像const uniqueObjects = [... new Set(array.map(obj => obj.age))] .map(age => {return array.find(obj => obj.age === age)})

–哈雷B
19/12/6在12:16



哈雷b是最大赢家

–米奇·格雷
8月22日16:56

#7 楼

对于那些想通过键返回具有唯一属性的对象的人




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

const key = 'age';

const arrayUniqueByKey = [...new Map(array.map(item =>
  [item[key], item])).values()];

console.log(arrayUniqueByKey);

   /*OUTPUT
       [
        { "name": "Bob", "age": 17 },
        { "name": "Carl", "age": 35 }
       ]
   */

 // Note: this will pick the last duplicated item in the list. 




评论


注意:这将选择列表中的最后一个重复项。

–尤金·库拉布霍夫(Eugene Kulabuhov)
1月19日20:10

将此添加到答案中!

–阿伦·塞尼(Arun Saini)
1月20日4:34

正是我想要的。多谢分享!

–德夫纳
3月16日20:46

我认为这应该是最重要的答案!

–贾罗斯拉夫·本茨(Jaroslav Benc)
5月5日13:52

#8 楼

我只是映射并删除了公仔:

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循环将更加“高效”。

评论


@elclanrs-“最高效的性能方法”-这种方法很慢。

–特拉维斯J
13年2月28日在1:47

@Eevee:我明白了。您的答案中的下划线版本也不是很快,我的意思是,最终您选择更方便的方法,我怀疑1-30%的结果或多或少会反映出通用测试中的“巨大改进”以及当OP / s达到数千时。

–elclanrs
13年2月28日在1:58



好吧,当然。仅包含三个元素,最快的事情是制作三个变量并使用一些if。三百万,您会得到一些非常不同的结果。

–伊芙
13年2月28日在2:06

速度不快,但就我而言,它确实成功了,因为我没有处理大型数据集,谢谢。

–Felipe Alarcon
18-2-4在15:26

#9 楼

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))];

// or with lodash

var unique = _.uniq(_.map(array, 'age'));


ES6示例

const data = [
  { name: "Joe", age: 17}, 
  { name: "Bob", age: 17}, 
  { name: "Carl", age: 35}
];

const arr = data.map(p => p.age); // [17, 17, 35]
const s = new Set(arr); // {17, 35} a set removes duplications, but it's still a set
const unique = [...s]; // [17, 35] Use the spread operator to transform a set into an Array
// or use Array.from to transform a set into an array
const unique2 = Array.from(s); // [17, 35]


评论


尽管此代码可以回答问题,但提供有关如何以及为什么解决问题的其他上下文将提高​​答案的长期价值。

–亚历山大
18/12/16在16:52

第一个使用indexOf vs索引的方法非常出色。

– alas
19年11月20日在17:38

请注意,这仅适用于原始值。如果您有一系列日期,则需要更多自定义方法。在此处阅读更多信息:codeburst.io/javascript-array-distinct-5edc93501dc4

– Molx
5月1日6:28

#10 楼

已经有很多有效的答案,但是我想添加一个仅使用reduce()方法的答案,因为它很干净而且很简单。

function uniqueBy(arr, prop){
  return arr.reduce((a, d) => {
    if (!a.includes(d[prop])) { a.push(d[prop]); }
    return a;
  }, []);
}


像这样使用它:

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

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


#11 楼

@ travis-j的答案的forEach版本(在现代浏览器和Node JS世界中非常有用):

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:// jsperf.com/filter-versus-dictionary/3

还有一个采用mapper函数的通用解决方案(tad比直接映射慢,但这是可以预期的):

 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]
 


评论


我最喜欢通用解决方案,因为在现实世界中,年龄不可能是唯一需要的唯一价值。

–盗窃
13年11月1日在11:17

在泛型中,将“ distinct.push(key)”更改为“ distinct.push(x)”以返回实际元素的列表,我发现这些元素非常有用!

–西拉斯·汉森(Silas Hansen)
2014年8月28日在12:56

#12 楼

默认情况下,我已经开始在所有新项目中都使用Underscore,以使我永远不必考虑这些小数据浪费问题。

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]

#13 楼

这是解决此问题的另一种方法:

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


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


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

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

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

方法1(TS的解决方案)非常快。有趣的是,方法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似乎在优化方面做得最好。

我不确定上述片段为何如此之快,也许有人比我有智慧。 ;-)

#14 楼

使用lodash

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


评论


您能否按照问题中的说明解释如何使用纯JavaScript?

–罗斯金
2015年1月21日,14:06

这是一个下划线功能_.chain

– Vini
2015年5月28日19:00

但是pluck()不在lodash中。

– Yash Vekaria
16年11月29日在10:57

_.chain(array).map('age')。unique()。value();为我工作。

– Yash Vekaria
16-11-29在11:07

版本4.0有一些重大更改,请参阅-stackoverflow.com/a/31740263/4050261

– Adarsh Madrecha
18年3月2日在22:19

#15 楼

使用Lodash

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

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


返回[17,35]

#16 楼

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;
}


#17 楼




 const x = [
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"},
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"}
].reduce(
  (accumulator, current) => {
    if(!accumulator.some(x => x.id === current.id)) {
      accumulator.push(current)
    }
    return accumulator;
  }, []
)

console.log(x)

/* output 
[ 
  { id: '93', name: 'CVAM_NGP_KW' },
  { id: '94', name: 'CVAM_NGP_PB' } 
]
*/ 




评论


这看起来像O(n ^ 2)

–cegprakash
19-09-5在12:56

#18 楼

underscore.js
_.uniq(_.pluck(array,"age"))

评论


请添加有关此答案的解释。

–机器人猫
16年5月2日在17:43

_.pluck已被删除以支持_.map

– Ajax3.14
16-09-25在9:42

#19 楼

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

项:数组

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

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;
    }, []);
}


用法

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


您可以将其添加到数组中原型,如果您的样式合适,则省略items参数。

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


您还可以使用Set而不是Array来加快匹配速度。

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        acc.add(item);
        return acc;
    }, new Set());
}


#20 楼

使用Maps的简单独特过滤器:



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

let data = new Map();

for (let obj of array) {
  data.set(obj.age, obj);
}

let out = [...data.values()];

console.log(out); 




#21 楼

您可能对基于其中一个键的唯一对象集感兴趣:
let array = 
[
    {"name":"Joe", "age":17}, 
    {"name":"Bob", "age":17}, 
    {"name":"Carl", "age": 35}
]
let unq_objs = [...new Map(array.map(o =>[o["age"], o])).values()];
console.log(unq_objs)
//result
[{name: "Bob", age: 17},
{name: "Carl", age: 35}]


#22 楼

刚发现这个问题,我认为它很有用

_.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})


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

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


#23 楼

我认为您正在寻找groupBy函数(使用Lodash)

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


产生结果:

17,35


jsFiddle演示:http://jsfiddle.net/4J2SX/201/

#24 楼

[...new Set([
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
  ].map(({ age }) => age))]


#25 楼

如果您具有Array.prototype.includes或愿意对其进行填充,则可以使用:

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


#26 楼

我知道我的代码虽然长度短,时间复杂,但是可以理解,所以我尝试了这种方式。

我试图在此处开发基于原型的函数,并且代码也会更改。

这里是我自己的原型函数。




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

  Array.prototype.Distinct = () => {
    var output = [];
    for (let i = 0; i < array.length; i++) {
      let flag = true;
      for (let j = 0; j < output.length; j++) {
        if (array[i].age == output[j]) {
          flag = false;
          break;
        }
      }
      if (flag)
        output.push(array[i].age);
    }
    return output;
  }
  //Distinct is my own function
  console.log(array.Distinct());
</script> 




#27 楼

如果像我一样,您希望在不影响速度的情况下提供更“实用”的功能,则本示例使用包裹在reduce闭包内的快速字典查找。

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]
    }
}, [{},[]])


根据此测试,我的解决方案是两次与建议的答案一样快

#28 楼

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

var ages = array.map(function(o){return o.age}).filter(function(v,i,a) {
    return a.indexOf(v)===i
});


评论


@ Jeb50关心添加易于阅读的多行吗?看着这里的其他人,我真的觉得它们不容易阅读或理解。我认为最好将其放在描述其功能的函数中。

– Craig
19-09-24在11:26



带有箭头功能:array.map(o => o.age).filter((v,i,a)=> a.indexOf(v)=== i)我现在很少使用function关键字,因此我一看就必须阅读两次read

–德雷奈
19-10-30在23:06



#29 楼

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

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);```


#30 楼

我用TypeScript写了自己的文章,例如Kotlin的Array.distinctBy {} ...

 function distinctBy<T, U extends string | number>(array: T[], mapFn: (el: T) => U) {
  const uniqueKeys = new Set(array.map(mapFn));
  return array.filter((el) => uniqueKeys.has(mapFn(el)));
}
 


当然,U是可哈希的。对于对象,您可能需要https://www.npmjs.com/package/es6-json-stable-stringify

评论


这实际上有效吗?您的数组过滤器会检查元素的键是否在唯一键的集合中,就算重复也不会总是如此吗?

–Rudey
12月18日10:12