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

Rol*_*ndo 325 javascript arrays unique

假设我有以下内容:

var array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]
Run Code Online (Sandbox Code Playgroud)

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

[17, 35]
Run Code Online (Sandbox Code Playgroud)

有没有办法我可以选择构造数据或更好的方法,这样我就不必遍历每个数组检查"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)
Run Code Online (Sandbox Code Playgroud)

Vla*_*den 475

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

const unique = [...new Set(array.map(item => item.age))];
Run Code Online (Sandbox Code Playgroud)

是一个如何做到一点的例子.

  • @Russell Vea几乎逐字地提供了这个解决方案[/sf/answers/2345859141/].你检查了现有的答案吗?但话说再说一次,266 +票表明其他人都没有检查过. (12认同)
  • 我收到一个错误:`TypeError :(中间值).slice不是函数` (11认同)
  • 对于打字稿你必须使用包装在Array.from()中的新Set ...我将在下面提供答案.@AngJobs (9认同)
  • @Thomas ...意味着传播运营商.在这种情况下,它意味着创建新集并将其传播到列表中.你可以在这里找到更多相关信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator (7认同)
  • 是否有可能根据对象中的多个键找出唯一对象? (4认同)
  • 谢谢,但是......意味着什么? (3认同)
  • 虽然您的答案本身是正确的,但它错过了关键用例 - 您希望保留其他对象属性而不仅仅是值。我花了相当多的时间进行搜索,但是 lodash uniqBy 函数应该可以实现这一点,这里有一个普通实现的链接:/sf/ask/2856094461/ (2认同)

Aru*_*ini 143

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

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.
Run Code Online (Sandbox Code Playgroud)

  • 一项改进 - 在地图调用之前添加 `.filter(Boolean)`。如果数组中的任何对象为“null”,那么这将抛出“无法读取 null 的属性(读取“id”)” (6认同)
  • 这个答案确实教会了我一些东西。谢谢你!@阿伦塞尼 (2认同)

小智 139

使用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]
Run Code Online (Sandbox Code Playgroud)

  • 这就是你必须继续滚动的原因 (8认同)
  • 如果我想用它的索引获取值,那么我怎么能得到示例:我想得到 {"name": "Bob", "age":"17"},{"name": "Bob", "age ":"17"} (4认同)
  • ps: .filter 内的 .map 可能会很慢,所以要小心大数组 (4认同)
  • @AbdullahAlMamun你可以在.filter中使用map而不是首先调用.map,如下所示:`array.filter((value,index,self)=> self.map(x => x.age).indexOf(value.age )==索引)` (3认同)
  • @IvanNosov 你的代码片段非常好,尽管几行解释它如何与指向地图和过滤文档的指针一起工作将使初学者变得完美和清晰 (3认同)
  • 这非常低效,使用 Set 是正确的方法 (2认同)
  • 此外,我测试了超过 48000 个对象数组以获得独特的结果。这个答案给出了最快的结果。谢啦。 (2认同)

Nie*_*sol 117

如果这是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);
}
Run Code Online (Sandbox Code Playgroud)

  • 不,因为`array_unique`会比较整个项目,而不仅仅是这里要求的年龄. (15认同)
  • 如果它是PHP你会使用`array_unique`,不是吗? (9认同)
  • 我认为`flags = {}`比`flags = []`更好 (6认同)
  • @zhuguowei也许,虽然稀疏数组没有什么问题 - 我还假设`age`是一个相对较小的整数(肯定是<120) (3认同)

Tra*_*s J 116

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

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;
}
Run Code Online (Sandbox Code Playgroud)

这是一个工作演示:http://jsfiddle.net/jbUKP/1

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

性能

http://jsperf.com/filter-versus-dictionary当我运行这本词典的速度提高了30%.

  • 哇,表现真的改变了,有利于地图选项.点击那个jsperf链接! (5认同)
  • 快速,无需任何额外的插件.真的很酷 (4认同)
  • 怎么样的`if(typeof(unique [array [i] .age])=="undefined"){distinct.push(array [i] .age); unique [array [i] .age] = 0; }` (2认同)
  • @98percentmonkey - 该对象用于促进“字典方法”。在对象内部,每个被跟踪的事件都被添加为一个键(或一个 bin)。这样,当我们遇到一个事件时,我们可以检查key(或bin)是否存在;如果它确实存在,我们知道该事件不是唯一的 - 如果它不存在,我们知道该事件是唯一的并将其添加进去。 (2认同)
  • @ 98percentmonkey - 使用对象的原因是查找是 O(1),因为它检查一次属性名称,而数组是 O(n),因为它必须查看每个值以检查是否存在。在过程结束时,对象中的键(箱)集表示唯一出现的集。 (2认同)

Chr*_*hew 68

这就是你如何使用新的Set by ES6 for Typescript来解决这个问题,截至2017年8月25日

Array.from(new Set(yourArray.map((item: any) => item.id)))
Run Code Online (Sandbox Code Playgroud)

  • 这有所帮助,感谢之前。类型“设置”不是数组类型 (3认同)

小智 67

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

const uniqueAges = [...new Set( array.map(obj => obj.age)) ];
Run Code Online (Sandbox Code Playgroud)

  • 像 `const uniqueObjects = [ ...new Set( array.map( obj =&gt; obj.age) ) ].map( Age=&gt; { return array.find(obj =&gt; obj.age === Age) } )` (9认同)
  • 这种方法仅适用于原始类型(在这种情况下,年龄是数字数组),但对于对象将失败。 (3认同)

elc*_*nrs 59

我只是映射并删除重复:

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]
Run Code Online (Sandbox Code Playgroud)

编辑: Aight!在性能方面不是最有效的方式,而是最简单最易读的IMO.如果您真的关心微优化或者您拥有大量数据,那么常规for循环将更加"高效".

  • 好吧,当然.只有_three elements_,最快的是制作三个变量并使用一些`if`s.三百万你会得到一些非常不同的结果. (4认同)
  • @elclanrs - "性能最有效的方式" - 这种方法是*慢*. (3认同)
  • @Eevee:我明白了。你的答案中的下划线版本是[也不是很快](http://jsperf.com/underscore-vs-no-udnerscore),我的意思是,最后你选择哪个更方便,我怀疑多1-30%或更少反映了通用测试和 OP/s 数以千计时的“巨大改进”。 (2认同)
  • 不快,但在我的情况下,它成功了,因为我没有处理大型数据集,谢谢。 (2认同)

Cip*_*ian 54

如果您需要整个对象,这与 ES6 版本略有不同:

let arr = [
    {"name":"Joe", "age":17}, 
    {"name":"Bob", "age":17}, 
    {"name":"Carl", "age": 35}
]
arr.filter((a, i) => arr.findIndex((s) => a.age === s.age) === i) // [{"name":"Joe", "age":17}, {"name":"Carl", "age": 35}]
Run Code Online (Sandbox Code Playgroud)


7gu*_*uyo 32

您可能对基于其中一个键的唯一对象集感兴趣:

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

const distinctItems = [...new Map(array.map(item => [item["age"], item])).values()];
Run Code Online (Sandbox Code Playgroud)

结果:

[
    {name: "Bob", age: 17},
    {name: "Carl", age: 35}
]
Run Code Online (Sandbox Code Playgroud)


ole*_*eac 31

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'));
Run Code Online (Sandbox Code Playgroud)

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]
Run Code Online (Sandbox Code Playgroud)

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

Rub*_*lix 21

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' } 
]
*/
Run Code Online (Sandbox Code Playgroud)

  • 这看起来像 O(n^2) (3认同)

Mrc*_*ief 20

forEach@特拉维斯-J的答案的版本(在现代的浏览器有用和节点JS世界):

var unique = {};
var distinct = [];
array.forEach(function (x) {
  if (!unique[x.age]) {
    distinct.push(x.age);
    unique[x.age] = true;
  }
});
Run Code Online (Sandbox Code Playgroud)

Chrome v29.0.1547上的速度提高了34%:http://jsperf.com/filter-versus-dictionary/3

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

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]
Run Code Online (Sandbox Code Playgroud)

  • 在泛型中,将"distinct.push(key)"更改为"distinct.push(x)"以返回实际元素的列表,我发现它非常有用! (5认同)

Har*_*ens 17

已经有很多有效的答案,但我想添加一个只使用该reduce()方法的答案, 因为它简洁明了.

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

像这样使用它:

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

var ages = uniqueBy(array, "age");
console.log(ages); // [17, 35]
Run Code Online (Sandbox Code Playgroud)


Boa*_*ges 14

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);
Run Code Online (Sandbox Code Playgroud)

  • 很好,:)好像你复制了我的答案:/sf/ask/1058814431/#58429784 (2认同)

Eev*_*vee 13

我开始坚持下划线默认情况下,所有新项目,这样我从来没有去想这些小数据改写(munging)的问题.

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());
Run Code Online (Sandbox Code Playgroud)

生产[17, 35].


BaM*_*BaM 13

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

var result = {};
for(var i in array) {
    result[array[i].age] = null;
}
result = Object.keys(result);
Run Code Online (Sandbox Code Playgroud)

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


编辑:好的,上面似乎是所有这里最慢的解决方案.

我在这里创建了一个性能测试用例: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);
}
Run Code Online (Sandbox Code Playgroud)

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

我不确定为什么上面的片段与其他片段相比如此之快,也许比我更聪明的人有答案.;-)


Ali*_*igy 13

我有一个小解决方案

let data = [{id: 1}, {id: 2}, {id: 3}, {id: 2}, {id: 3}];

let result = data.filter((value, index, self) => self.findIndex((m) => m.id === value.id) === index);
Run Code Online (Sandbox Code Playgroud)


Zee*_*Zee 9

使用 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);
Run Code Online (Sandbox Code Playgroud)


小智 8

使用lodash

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

  • 你能解释如何使用普通的javascript来解决这个问题吗? (2认同)
  • 但是pluck()不在lodash中. (2认同)

Dmi*_*nov 6

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;
}
Run Code Online (Sandbox Code Playgroud)


小智 6

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

  • 请添加解释如何回答问题的说明. (5认同)
  • _.pluck 已被移除以支持 _.map (2认同)

Yas*_*ria 6

使用Lodash

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

_.chain(array).map('age').unique().value();
Run Code Online (Sandbox Code Playgroud)

退货[17,35]


Seb*_*son 6

如果您想返回一个唯一的对象列表。这是另一种选择:

const unique = (arr, encoder=JSON.stringify, decoder=JSON.parse) =>
  [...new Set(arr.map(item => encoder(item)))].map(item => decoder(item));
Run Code Online (Sandbox Code Playgroud)

这将变成:

unique([{"name": "john"}, {"name": "sarah"}, {"name": "john"}])
Run Code Online (Sandbox Code Playgroud)

进入

[{"name": "john"}, {"name": "sarah"}]
Run Code Online (Sandbox Code Playgroud)

这里的技巧是,我们首先使用 将项目编码为字符串JSON.stringify,然后将其转换为 Set(这使得字符串列表唯一),然后使用 将其转换回原始对象JSON.parse


Jai*_*son 6

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

      const ages = [...new Set(array.reduce((a, c) => [...a, c.age], []))];
    
      console.log(ages);
Run Code Online (Sandbox Code Playgroud)


Geo*_*rov 6

如果您想迭代独特的项目,请使用此:(
更灵活的版本/sf/answers/4090084911/

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

const uniqBy = (arr, selector = (item) => item) => {
    const map = new Map();
    arr.forEach((item) => {
        const prop = selector(item);
        if (!map.has(prop)) map.set(prop, item);
    });
    return [...map.values()];
}

const uniqItems = uniqBy(array, (item) => item.age);

console.log('uniqItems: ', uniqItems);
Run Code Online (Sandbox Code Playgroud)

如果您只需要唯一值,请使用此:(
重复/sf/answers/2456479161/,只是为了完整性)

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

const uniq = (items) => [...new Set(items)];

const uniqAges = uniq(array.map((item) => item.age));

console.log('uniqAges: ', uniqAges);
Run Code Online (Sandbox Code Playgroud)


Ste*_*gin 5

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

项目:数组

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

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;
    }, []);
}
Run Code Online (Sandbox Code Playgroud)

用法

const distinctLastNames = distinct(items, (item)=>item.lastName);
const distinctItems = distinct(items);
Run Code Online (Sandbox Code Playgroud)

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

const distinctLastNames = items.distinct( (item)=>item.lastName) ) ;
const distinctItems = items.distinct() ;
Run Code Online (Sandbox Code Playgroud)

您也可以使用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());
}
Run Code Online (Sandbox Code Playgroud)