如何从JavaScript中的对象数组中获取不同的值?
假设我有以下几点:
var array = [ {"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35} ]
能够获得所有不同年龄段的数组的最佳方法是什么,以便我得到一个结果数组:
[17, 35]
有没有什么办法可以select性地构造数据或更好的方法,这样我就不必遍历每个数组检查“年龄”的值,并检查另一个数组的存在,并添加它,如果不是?
如果有某种方式,我可以把不同的年龄拉出来…
目前我想要改进的方法不够…如果这意味着不是将“数组”作为一个对象数组,而是使用某个唯一键(即“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)
如果这是PHP我会build立一个数组与键,并在最后采取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); }
如果您使用ES6 / ES2015或更高版本,则可以这样做:
var unique = [...new Set(array.map(item => item.age))];
这里是一个如何做到这一点的例子。
基于@Jonas Kello编辑build议:
const unique = [...new Set(array.map(item => item.age))];
你可以使用像这样的字典方法。 基本上你把你想要分离的值作为字典中的键。 如果该键不存在,则将该值添加为不同的值。
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)更快的方法,因为您必须检查每个值至less一次。
性能
我只是映射和删除dups:
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]
编辑: Aight! 不是性能方面最有效的方式,而是最简单的最易读的IMO。 如果你真的关心微观优化或者你有大量的数据,那么定期for
循环会更加“高效”。
使用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]
forEach
版本的@travis-j的答案(对现代浏览器和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
一个通用的解决scheme,需要一个映射函数(稍微比直接映射,但这是预期的):
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]
我已经开始在所有新项目中默认使用Underscore,所以我从来不需要考虑这些小小的数据pipe理问题。
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]
17,35 [17, 35]
。
使用lodash
var array = [ { "name": "Joe", "age": 17 }, { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 } ]; _.chain(array).pluck('age').unique().value(); > [17, 35]
使用ES6function,您可以执行如下操作:
const filteredValues = [...new Set( array.map(obj => obj.name)) ];
underscore.js _.uniq(_.pluck(array,"age"))
这是解决这个问题的另一种方法:
var result = {}; for(var i in array) { result[array[i].age] = null; } result = Object.keys(result);
我不知道这个解决scheme与其他解决scheme的速度有多快,但我喜欢更清洁的外观。 😉
编辑:好吧,上面似乎是所有这里最慢的解决scheme。
我在这里创build了一个性能testing用例: http : //jsperf.com/distinct-values-from-array
而不是testing年龄(整数),我select比较名称(string)。
方法1(TS的解决scheme)非常快。 有趣的是,方法7胜过所有其他的解决scheme,在这里我刚摆脱.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在优化方面做得最好。
我不确定为什么上面的代码片段与其他代码相比如此之快,也许有人比我更聪明一个答案。 😉
使用Lodash
var array = [ { "name": "Joe", "age": 17 }, { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 } ]; _.chain(array).map('age').unique().value();
退货[17,35]
这是你如何解决这个问题,从2017年8月25日起,使用新的设置通过ES6打字稿
Array.from(new Set(yourArray.map((item: any) => item.id)))
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; }
刚刚发现这一点,我认为这是有用的
_.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
是以stringforms返回的,所以如果你需要整数,你应该这样做
_.map(_.indexBy(records, '_id'), function(obj,key){return parseInt(key)})
如果像我一样,你更喜欢一个更“function”而不影响速度,这个例子使用快速字典查找包装内减less封闭。
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] } }, [{},[]])
根据这个testing,我的解决scheme是build议答案的两倍
我认为你正在寻找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/
如果你有Array.prototype.includes或愿意填充它,这工作:
var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.push(x.age); });
这是一个多用途的解决scheme,使用减less,允许映射和维护插入顺序。
项目 :一个数组
映射器(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);
你可以添加这个到你的数组原型,如果这是你的风格,可以省略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()); }
我在这个function上的两分钱:
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://jsperf.com/distinct-values-from-array/3
使用新的Ecmafunction是伟大的,但并不是所有的用户都有可用的。
以下代码将附加一个名为distinct的新函数给全局数组对象。 如果您正在尝试获取对象数组的不同值,则可以传递该值的名称以获取该types的不同值。
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;};
看看我的post CodePen的演示。
你试一试
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);
使用d3.js v3 :
ages = d3.set( array.map(function (d) { return d.age; }) ).values();
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; }
这个函数可以有唯一的数组和对象
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; }
像这样使用
tags_obj = [{name:"milad"},{name:"maziar"},{name:"maziar"}] tags_arr = ["milad","maziar","maziar"] console.log(oaunic(tags_obj,"name")) //for object console.log(oaunic(tags_arr)) //for array