如何从 JavaScript 中的对象数组中获取不同的值?
Posted
技术标签:
【中文标题】如何从 JavaScript 中的对象数组中获取不同的值?【英文标题】:How to get distinct values from an array of objects in JavaScript? 【发布时间】:2013-02-14 01:50:00 【问题描述】:假设我有以下几点:
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, label: "Hello", color: "red", sort: 0 , id: 2, label: "World" , 颜色:“绿色”,排序:1 , id:3,标签:“Hello”,颜色:“蓝色”,排序:4 , id:4,标签:“阳光”,颜色:“黄色” , 排序: 5 , id: 5, label: "Hello", color: "red", 排序: 6 ], keys = ['label', 'color'], filters = listOfTags.filter( (s = > o => (k => !s.has(k) && s.add(k)) (keys.map(k => o[k]).join('|')) ) (new Set) ; console.log(过滤); 赏金很棒,但是这里已经回答了给定数据和答案的问题:***.com/questions/53542882/…。赏金的目的是什么?我应该用两个或更多键来回答这个特殊问题吗?Set
object 和 map
s 是浪费的。这项工作只需要一个简单的.reduce()
阶段。
请检查这个例子,***.com/a/58944998/13013258。
【参考方案1】:
使用iter-ops 库的高效且干净的方法:
import pipe, distinct, map from 'iter-ops';
const array =
[
name: 'Joe', age: 17,
name: 'Bob', age: 17,
name: 'Carl', age: 35
];
const i = pipe(
array,
distinct(a => a.age),
map(m => m.age)
);
const uniqueAges = [...i]; //=> [17, 35]
【讨论】:
【参考方案2】: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);
【讨论】:
【参考方案3】:let mobilePhones = [id: 1, brand: "B1", id: 2, brand: "B2", id: 3, brand: "B1", id: 4, brand: "B1", id: 5, brand: "B2", id: 6, brand: "B3"]
let allBrandsArr = mobilePhones .map(row=>
return row.brand;
);
let uniqueBrands = allBrandsArr.filter((item, index, arry) => (arry.indexOf(item) === index));
console.log('uniqueBrands ', uniqueBrands );
【讨论】:
【参考方案4】: var array =
[
"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"]
【讨论】:
【参考方案5】:const array = [
"name": "Joe",
"age": 17
,
"name": "Bob",
"age": 17
,
"name": "Carl",
"age": 35
]
const uniqueArrayByProperty = (array, callback) =>
return array.reduce((prev, item) =>
const v = callback(item);
if (!prev.includes(v)) prev.push(v)
return prev
, [])
console.log(uniqueArrayByProperty(array, it => it.age));
【讨论】:
【参考方案6】:我有一个小解决方案
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);
【讨论】:
【参考方案7】:如果您需要整个对象,这是 ES6 版本的轻微变化:
let arr = [
"name":"Joe", "age":17,
"name":"Bob", "age":17,
"name":"Carl", "age": 35
]
arr.filter((a, i) => arr.findIndex((s) => a.id === s.id) === i) // ["name":"Joe", "age":17, "name":"Carl", "age": 35]
【讨论】:
我执行了这个,它没有像描述的那样过滤,结果是 // ["name":"Joe", "age":17]【参考方案8】:原始类型
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)
【讨论】:
【参考方案9】: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);
【讨论】:
使用reduce和set的好方法。谢谢 太棒了,我怎样才能得到每个项目的计数? 感谢您的评论,也许我没有完全理解您的要求,我假设您正在查看以下内容 - ***.com/a/66002712/1374554【参考方案10】:假设我们有类似arr=[id:1,age:17,id:2,age:19 ...]
这样的数据,那么我们可以找到这样的唯一对象 -
function getUniqueObjects(ObjectArray)
let uniqueIds = new Set();
const list = [...new Set(ObjectArray.filter(obj =>
if (!uniqueIds.has(obj.id))
uniqueIds.add(obj.id);
return obj;
))];
return list;
在这里查看Codepen Link
【讨论】:
【参考方案11】:如果你的数组是对象数组,你可以使用这个代码。
getUniqueArray = (array: MyData[]) =>
return array.filter((elem, index) => array.findIndex(obj => obj.value == elem.value) === index);
MyData 如下:
export interface MyData
value: string,
name: string
注意:您不能使用 Set,因为在比较对象时,它们是通过引用而不是值进行比较。因此,您需要唯一键来比较对象,在我的示例中,唯一键是值字段。 更多详情,您可以访问此链接:Filter an array for unique values in javascript
【讨论】:
【参考方案12】:如果您无法使用 ES5,或者由于某种原因不能使用 new Set
或 new Map
,并且您需要一个包含具有唯一键值的数组(而不仅仅是一组唯一键),您可以使用以下内容:
function distinctBy(key, array)
var keys = array.map(function (value) return value[key]; );
return array.filter(function (value, index) return keys.indexOf(value[key]) === index; );
或 TypeScript 中的类型安全等效项:
public distinctBy<T>(key: keyof T, array: T[])
const keys = array.map(value => value[key]);
return array.filter((value, index) => keys.indexOf(value[key]) === index);
用法:
var distinctPeople = distinctBy('age', people);
所有其他答案:
返回唯一键数组而不是对象(例如返回年龄列表而不是具有唯一年龄的人); 使用您可能无法使用的 ES6、new Set
、new Map
等;
没有可配置的键(例如将 .age
硬编码到 distinct 函数中);
假设键可用于索引数组,但这并不总是正确的,TypeScript 不允许这样做。
这个答案没有上述四个问题中的任何一个。
【讨论】:
【参考方案13】: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)【参考方案14】:我随机抽取样本并针对以下 100,000 个项目进行测试:
let array=[]
for (var i=1;i<100000;i++)
let j= Math.floor(Math.random() * i) + 1
array.push("name":"Joe"+j, "age":j)
这里是每个的性能结果:
Vlad Bezden Time: === > 15ms
Travis J Time: 25ms === > 25ms
Niet the Dark Absol Time: === > 30ms
Arun Saini Time: === > 31ms
Mrchief Time: === > 54ms
Ivan Nosov Time: === > 14374ms
另外,我想提一下,由于项目是随机生成的,第二个地方是在 Travis 和 Niet 之间进行迭代。
【讨论】:
【参考方案15】:如果你使用的是 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+ 票表明也没有其他人检查过。【参考方案16】:
使用地图的简单不同过滤器:
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);
【讨论】:
【参考方案17】:如果您想返回一个唯一的对象列表。 这是另一种选择:
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(这使得字符串列表唯一),然后将其转换回原始对象使用JSON.parse
。
【讨论】:
【参考方案18】:有linq.js - LINQ for JavaScript 包(npm install linq),.Net 开发人员应该很熟悉。
在samples 中显示的其他方法中,有明显的重载。
通过属性值从对象数组中区分对象的示例 是
Enumerable.from(array).distinct(“$.id”).toArray();
来自https://medium.com/@xmedeko/i-recommend-you-to-try-https-github-com-mihaifm-linq-20a4e3c090e9
【讨论】:
【参考方案19】:从 2017 年 8 月 25 日起,您将通过 ES6 为 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)))
【参考方案20】:
使用集合和过滤器。这保留了顺序:
let unique = (items) =>
const s = new Set();
return items.filter((item) =>
if (s.has(item))
return false;
s.add(item);
return true;
);
console.log(
unique(
[
'one', 'two', 'two', 'three', 'three', 'three'
]
)
);
/*
output:
[
"one",
"two",
"three"
]
*/
【讨论】:
【参考方案21】:您可以使用像这样的字典方法。基本上,您将想要区分的值分配为“字典”中的键(这里我们使用数组作为对象以避免字典模式)。如果键不存在,则将该值添加为 distinct。
这是一个工作演示:
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) 更快的方法了,因为您必须至少检查每个值一次。
之前的版本使用了一个对象和 for in。这些在本质上是次要的,并且已经在上面进行了次要更新。但是,原始 jsperf 中两个版本之间的性能似乎有所提高的原因是数据样本量非常小。因此,之前版本的主要比较是查看内部映射和过滤器使用与字典模式查找之间的差异。
如上所述,我已经更新了上面的代码,但是,我还更新了 jsperf 以查看 1000 个对象而不是 3 个。3 忽略了许多涉及的性能缺陷 (obsolete jsperf)。
性能
https://jsperf.com/filter-vs-dictionary-more-data 当我运行这本词典时,速度快了 96%。
【讨论】:
快速,无需任何额外插件。真的很酷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)表示唯一出现的一组。【参考方案22】:
已经有很多有效的答案,但我想添加一个只使用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]
【讨论】:
【参考方案23】:简单的单线具有出色的性能。比我 tests 中的 ES6 解决方案快 6%。
var ages = array.map(function(o)return o.age).filter(function(v,i,a)
return a.indexOf(v)===i
);
【讨论】:
@Jeb50 想添加一个易于阅读的多行吗?看看这里的其他人,我真的不觉得它们易于阅读或理解。我认为最好将其放在描述其功能的函数中。 带箭头功能:array.map( o => o.age).filter( (v,i,a) => a.indexOf(v)===i)
。我现在很少使用 function 关键字,以至于我看到它时必须阅读两次?【参考方案24】:
对于那些想要返回所有属性唯一的对象
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.
【讨论】:
您的解决方案是完美的/现代的【参考方案25】:const array = [
"name": "Joe", "age": 17 ,
"name":"Bob", "age":17 ,
"name":"Carl", "age": 35
]
const allAges = array.map(a => a.age);
const uniqueSet = new Set(allAges)
const uniqueArray = [...uniqueSet]
console.log(uniqueArray)
【讨论】:
【参考方案26】:从一组键中获取不同值的集合的方法。
您可以从here 获取给定的代码,并为只需要的键添加映射以获得唯一对象值的数组。
const
listOfTags = [ id: 1, label: "Hello", color: "red", sorting: 0 , id: 2, label: "World", color: "green", sorting: 1 , id: 3, label: "Hello", color: "blue", sorting: 4 , id: 4, label: "Sunshine", color: "yellow", sorting: 5 , id: 5, label: "Hello", color: "red", sorting: 6 ],
keys = ['label', 'color'],
filtered = listOfTags.filter(
(s => o =>
(k => !s.has(k) && s.add(k))
(keys.map(k => o[k]).join('|'))
)(new Set)
)
result = filtered.map(o => Object.fromEntries(keys.map(k => [k, o[k]])));
console.log(result);
.as-console-wrapper max-height: 100% !important; top: 0;
【讨论】:
【参考方案27】:回答这个老问题毫无意义,但是有一个简单的答案可以说明 Javascript 的本质。 Javascript 中的对象本质上是哈希表。我们可以使用它来获取唯一键的哈希:
var o = ; array.map(function(v) o[v.age] = 1; );
然后我们可以将散列简化为一个唯一值数组:
var a2 = []; for (k in o) a2.push(k);
这就是你所需要的。数组 a2 仅包含唯一的年龄。
【讨论】:
【参考方案28】:那么你可以使用 lodash 来编写一个不那么冗长的代码
方法 1:嵌套方法
let array =
[
"name":"Joe", "age":17,
"name":"Bob", "age":17,
"name":"Carl", "age": 35
]
let result = _.uniq(_.map(array,item=>item.age))
方法2:方法链接或级联方法
let array =
[
"name":"Joe", "age":17,
"name":"Bob", "age":17,
"name":"Carl", "age": 35
]
let result = _.chain(array).map(item=>item.age).uniq().value()
您可以从https://lodash.com/docs/4.17.15#uniq 阅读有关 lodash 的 uniq() 方法的信息
【讨论】:
【参考方案29】:我知道我的代码长度和时间复杂度都很短,但是可以理解,所以我尝试了这种方式。
我正在尝试在这里开发基于原型的功能,并且代码也发生了变化。
这里,Distinct是我自己的原型函数。
<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>
【讨论】:
【参考方案30】:这里有很多很好的答案,但没有一个解决了以下问题:
有什么方法可以替代地构建数据
我会创建一个对象,其键是年龄,每个都指向一个名称数组。
var array = [ "name": "Joe", "age": 17 , "name": "Bob", "age": 17 , "name": "Carl", "age": 35 ];
var map = array.reduce(function(result, item)
result[item.age] = result[item.age] || [];
result[item.age].push(item.name);
return result;
, );
console.log(Object.keys(map));
console.log(map);
通过这种方式,您已将数据结构转换为一种非常容易从中检索不同年龄的数据结构。
这是一个更紧凑的版本,它还存储整个对象而不仅仅是名称(如果您正在处理具有超过 2 个属性的对象,因此它们不能存储为键和值)。
var array = [ "name": "Joe", "age": 17 , "name": "Bob", "age": 17 , "name": "Carl", "age": 35 ];
var map = array.reduce((r, i) => ((r[i.age] = r[i.age] || []).push(i), r), );
console.log(Object.keys(map));
console.log(map);
【讨论】:
以上是关于如何从 JavaScript 中的对象数组中获取不同的值?的主要内容,如果未能解决你的问题,请参考以下文章
如何从对象数组中获取键值列表-JavaScript [重复]