ChatGPT解决这个技术问题 Extra ChatGPT

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

假设我有以下内容:

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)
迭代不是“效率低下”,你不能对每个元素做任何“没有迭代”的事情。您可以使用各种看起来像函数的方法,但最终,在某种程度上必须迭代项目。
//100% 运行代码 const listOfTags = [{ id: 1, label: "Hello", color: "red",sorting: 0 }, { id: 2, label: "World", color: "green", 排序: 1 }, { id: 3, label: "Hello", color: "blue", 排序: 4 }, { id: 4, label: "Sunshine", color: "yellow", 排序: 5 }, { id : 5, label: "Hello", color: "red", sort: 6 }], keys = ['label', 'color'], filters = listOfTags.filter( (s => o => (k => !s.has(k) && s.add(k)) (keys.map(k => o[k]).join('|')) ) (new Set) );控制台.log(过滤);
赏金很棒,但是这里已经回答了具有给定数据和答案的问题:stackoverflow.com/questions/53542882/…。赏金的目的是什么?我应该用两个或更多键来回答这个特殊问题吗?
Set 对象和 map 是浪费的。这项工作只需要一个简单的 .reduce() 阶段。
请检查此示例,stackoverflow.com/a/58944998/13013258

V
Vlad Bezden

如果您使用的是 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']

Here 是有关如何执行此操作的示例。


我收到一个错误:TypeError: (intermediate value).slice is not a function
@Thomas ... 表示传播运算符。在这种情况下,这意味着创建新集合并将其分布在列表中。您可以在此处找到更多信息:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
对于打字稿,您必须使用包装在 Array.from() 中的新 Set ...我将在下面提供该答案。 @AngJobs
是否可以根据对象中的多个键找出唯一对象?
这个解决方案几乎是由@Russell Vea 逐字a few months earlier给出的。你检查了现有的答案吗?但话又说回来,266+ 票表明也没有其他人检查过。
A
Arun Saini

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

常量数组 = [ { "name": "Joe", "age": 17 }, { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 } ] 常量键 = '年龄'; const arrayUniqueByKey = [...new Map(array.map(item => [item[key], item])).values()]; console.log(arrayUniqueByKey); /*OUTPUT [ { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 } ] */ // 注意:这将选择最后一个重复的项目名单。


您的解决方案是完美的/现代的
太棒了 - 我正要搜索这个问题,或者自己写一个,然后在这里看到你的答案。
这种方法可以用于存储与唯一键具有相同值的多个项目吗?假设我希望输出如下所示:键 17 具有存储 BobJoe 对象的对象项数组的值。经过我的研究,它需要很少的迭代来得到我想要的。也许有一个我不知道的线性链接功能
你应该解释一下这种方法小@arun
一项改进——在地图调用之前添加 .filter(Boolean)。如果数组中的任何对象是 null,那么这将抛出 Cannot read properties of null (reading 'id')
I
Ivan Nosov

使用 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"}
这就是为什么你必须继续滚动
@AbdullahAlMamun 您可以在 .filter 中使用 map 而不是首先调用 .map ,如下所示:array.filter((value, index, self) => self.map(x => x.age).indexOf(value.age) == index)
ps: .filter 里面的 .map 可能会很慢,所以要小心大数组
@IvanNosov 您的代码段非常好,尽管几行解释它如何与指向映射和过滤文档的指针一起工作将使初学者完美而清晰
J
Jon Catmull

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

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

这种方法仅适用于原始类型(这里的情况是年龄是一个数字数组),但对于对象会失败。
知道如何使它适用于具有两个键的对象吗?
const uniqueObjects = [ ...new Set( array.map( obj => obj.age) ) ].map( age=> { return array.find(obj => obj.age === age) } ) 之类的东西
N
Niet the Dark Absol

如果这是 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 会比较整个项目,而不仅仅是这里询问的年龄。
我认为flags = {}flags = []
@zhguuowei 也许,虽然稀疏数组并没有什么问题——而且我还假设 age 是一个相对较小的整数(肯定小于 120)
我们如何还可以打印出相同年龄的总人数?
@NiettheDarkAbsol 你会怎么做这样的事情? jsfiddle.net/BeerusDev/asgq8n7e/28 我有一个包含对象的数组,每个键 Days 都有一个数组。
T
Travis J

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

这是一个工作演示:

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

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

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

如前所述,我已经更新了上面的代码,但是,我还更新了 jsperf 以查看 1000 个对象而不是 3 个。3 忽略了许多涉及的性能缺陷(obsolete jsperf)。

表现

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

https://i.stack.imgur.com/HZTBu.png


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

从 2017 年 8 月 25 日起,您将通过 ES6 for Typescript 使用新的 Set 来解决此问题

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

这有帮助,谢谢。类型“Set”不是数组类型
这是一个很好的答案。直到我向下滚动到这个,我打算写一个答案说: Object.keys(values.reduce((x, y) => {x[y] = null; return x; }, {} ));但是这个答案更简洁,适用于所有数据类型,而不仅仅是字符串。
非打字稿版本:Array.from(new Set(yourArray.map((item) => item.id)))
e
elclanrs

我只是映射和删除重复:

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 - “最高效的方式” - 这种方法很慢。
@Eevee:我明白了。您的答案中的下划线版本是not very fast either,我的意思是,最终您选择更方便的方式,我怀疑 1-30% 或多或少反映了通用测试中的“巨大改进”以及当 OP/s 为数千时。
好吧,当然。只有三个元素,最快的方法是创建三个变量并使用一些if。使用 300 万,你会得到一些非常不同的结果。
不快,但在我的情况下,它起到了作用,因为我没有处理大型数据集,谢谢。
这不是 O(N^2) 吗?不建议。您想使用恒定时间进行内部查找,例如地图。
o
oleg gabureac
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]

虽然此代码可能会回答问题,但提供有关其解决问题的方式和原因的额外上下文将提高答案的长期价值。
第一个使用 indexOf 与索引的方法非常棒。
请注意,这仅适用于原始值。如果您有一组日期,那么您需要一些更自定义的方法。在此处阅读更多信息:codeburst.io/javascript-array-distinct-5edc93501dc4
H
Harry Stevens

已经有很多有效的答案,但我想添加一个仅使用 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]

这比最佳答案慢吗?
R
Rubén Morales Félix

常量数组 = [ {"id":"93","name":"CVAM_NGP_KW"}, {"id":"94","name":"CVAM_NGP_PB"}, {"id":"93"," name":"CVAM_NGP_KW"}, {"id":"94","name":"CVAM_NGP_PB"} ] function uniq(array, field) { return array.reduce((accumulator, current) => { if(! accumulator.includes(current[field])) { accumulator.push(current[field]) } return accumulator; }, [] ) } const ids = uniq(array, 'id'); console.log(ids) /* 输出 ["93", "94"] */


这看起来像 O(n^2)
C
Ciprian

如果您需要整个对象,这与 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}]

我执行了这个,它没有像描述的那样过滤,结果是 // [{"name":"Joe", "age":17}]
将过滤器参数正确更新为 age。感谢您发现
M
Mike

我有一个小解决方案

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

M
Mrchief

@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

还有一个采用映射器函数的通用解决方案(比直接映射慢一点,但这是意料之中的):

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]

我最喜欢通用解决方案,因为年龄不太可能是现实世界场景中唯一需要的不同值。
在泛型中,将“distinct.push(key)”更改为“distinct.push(x)”以返回实际元素的列表,我发现它非常有用!
E
Eevee

我已经开始在所有新项目中默认使用 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]


M
MD Ashik

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

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

result = Object.keys(result);

或者

result = Object.values(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 在优化方面做得最好。

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


I
Ivan Nosov

使用 lodash

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

您能解释一下如何使用问题中暗示的普通 javascript 来执行此操作吗?
它是一个下划线函数_.chain
但是 pluck() 不在 lodash 中。
_.chain(array).map('age').unique().value();为我工作。
4.0 版有一些重大更改,请参阅 - stackoverflow.com/a/31740263/4050261
J
Jaison James

var 数组 = [ {"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35 } ];常量年龄 = [...new Set(array.reduce((a, c) => [...a, c.age], []))];控制台日志(年龄);


使用 reduce 和 set 的好方法。谢谢
太棒了,我怎样才能得到每个项目的计数?
感谢您的评论,也许我没有完全满足您的要求,我假设您正在查看以下内容 - stackoverflow.com/a/66002712/1374554
B
Boanerges

常量数组 = [ { "name": "Joe", "age": 17 }, { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 } ] 常量键 = '年龄'; const arrayUniqueByKey = [...new Map(array.map(item => [item[key], item])).values()]; console.log(arrayUniqueByKey);


这是一个繁荣的解决方案。
很好,:) 好像你复制了我的答案:stackoverflow.com/questions/15125920/…
Z
Zee

使用 Maps 的简单不同过滤器:

让数组 = [ {"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35 } ];让数据 = 新地图(); for (let obj of array) { data.set(obj.age, obj); } 放出 = [...data.values()];控制台.log(out);


Y
Yash Vekaria

使用 Lodash

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

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

返回 [17,35]


D
Dmitriy Kupriyanov
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;
}

R
Radosław Kopczyński

下划线.js _.uniq(_.pluck(array,"age"))


请添加对如何回答问题的解释。
_.pluck 已被删除以支持 _.map
S
Steven Spungin

这是一个使用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;
    }, []);
}

用法

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

如果这是您的风格,您可以将其添加到您的 Array 原型中并省略 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());
}

S
Sebastian Karlsson

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

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

这将变成这样:

unique([{"name": "john"}, {"name": "sarah"}, {"name": "john"}])

进入

[{"name": "john"}, {"name": "sarah"}]

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


c
craftsmannadeem

var 数组 = [ {"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35 } ] console.log(Object.keys(array.reduce((r,{age}) => (r[age]='', r) , {})))

输出:

Array ["17", "35"]

s
simo

刚找到这个,我认为它很有用

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

再次使用 underscore,所以如果您有这样的对象

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

A
Amil Sajeev

我认为您正在寻找 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/


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

f
felix_teg

原始类型

var unique = [...new Set(array.map(item => item.pritiveAttribute))];

对于复杂类型,例如对象

var unique = [...new DeepSet(array.map(item => item.Object))];

export class DeepSet extends Set {

  add (o: any) {
    for (let i of this)
      if (this.deepCompare(o, i))
        return this;
    super.add.call(this, o);
    return this;
  };

  private deepCompare(o: any, i: any) {
    return JSON.stringify(o) === JSON.stringify(i)
  }
}