jQuery 按属性创建唯一对象数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18773778/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Create array of unique objects by property
提问by theblueone
I created an array of objects like so:
我创建了一个对象数组,如下所示:
var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";
places.push(a);
var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";
places.push(b);
...
I'm trying to create a new array that filters the places to only contains objects that don't have the same city property (lat/lng duplicates are ok). Is there a built in JS or Jquery function to achieve this?
我正在尝试创建一个新数组,该数组将地点过滤为仅包含不具有相同城市属性的对象(lat/lng 重复项可以)。是否有内置的 JS 或 Jquery 函数来实现这一点?
回答by T.J. Crowder
I'd probably use a flags object during the filtering, like this:
我可能会在过滤过程中使用标志对象,如下所示:
var flags = {};
var newPlaces = places.filter(function(entry) {
if (flags[entry.city]) {
return false;
}
flags[entry.city] = true;
return true;
});
That uses Array#filter
from ECMAScript5 (ES5), which is one of the ES5 additions that can be shimmed (search for "es5 shim" for several options).
这使用Array#filter
了 ECMAScript5 (ES5),它是 ES5 中可以填充的添加项之一(搜索“es5 shim”以获得多个选项)。
You can do it without filter
, of course, it's just a bit more verbose:
你可以不用filter
,当然,它只是更冗长一点:
var flags = {};
var newPlaces = [];
var index;
for (index = 0; index < places.length; ++index) {
if (!flags[entry.city]) {
flags[entry.city] = true;
newPlaces.push(entry);
}
});
Both of the above assume the firstobject with a given city should be kept, and all other discarded.
以上都假设应该保留给定城市的第一个对象,而其他所有对象都应丢弃。
Note: As user2736012 points out below, my test if (flags[entry.city])
will be true for cities with names that happen to be the same as properties that exist on Object.prototype
such as toString
. Very unlikely in this case, but there are four ways to avoid the possibility:
注意:正如 user2736012 在下面指出的那样,我的测试if (flags[entry.city])
对于名称恰好Object.prototype
与toString
. 在这种情况下不太可能,但有四种方法可以避免这种可能性:
(My usual preferred solution) Create the object without a prototype:
var flags = Object.create(null);
. This is a feature of ES5. Note that this cannot be shimmed for obsolete browsers like IE8 (the single-argument version ofObject.create
can be exceptwhen that argument's value isnull
).Use
hasOwnProperty
for the test, e.g.if (flags.hasOwnProperty(entry.city))
Put a prefix on that you know doesn't exist for any
Object.prototype
property, such asxx
:var key = "xx" + entry.city; if (flags[key]) { // ... } flags[key] = true;
As of ES2015, you could use a
Set
instead:const flags = new Set(); const newPlaces = places.filter(entry => { if (flags.has(entry.city)) { return false; } flags.add(entry.city); return true; });
(我通常首选的解决方案)创建没有原型的对象:
var flags = Object.create(null);
. 这是 ES5 的一个特性。请注意,对于像 IE8 这样过时的浏览器,这不能被填充(除非该参数的值为 ,否则的单参数版本Object.create
可以是)。null
使用
hasOwnProperty
的测试,例如,if (flags.hasOwnProperty(entry.city))
在您知道任何
Object.prototype
属性不存在的前缀上添加前缀,例如xx
:var key = "xx" + entry.city; if (flags[key]) { // ... } flags[key] = true;
从 ES2015 开始,您可以使用 a
Set
代替:const flags = new Set(); const newPlaces = places.filter(entry => { if (flags.has(entry.city)) { return false; } flags.add(entry.city); return true; });
回答by IgorL
Shortest, but not best performance(see update bellow) solution for es6 :
es6 的最短但不是最佳性能(请参阅下面的更新)解决方案:
function unique(array, propertyName) {
return array.filter((e, i) => array.findIndex(a => a[propertyName] === e[propertyName]) === i);
}
performance: https://jsperf.com/compare-unique-array-by-property
回答by Alec Perkey
https://lodash.com/docs#uniqBy
https://lodash.com/docs#uniqBy
https://github.com/lodash/lodash/blob/4.13.1/lodash.js#L7711
https://github.com/lodash/lodash/blob/4.13.1/lodash.js#L7711
/**
* This method is like `_.uniq` except that it accepts `iteratee` which is
* invoked for each element in `array` to generate the criterion by which
* uniqueness is computed. The iteratee is invoked with one argument: (value).
*
* @static
* @memberOf _
* @since 4.0.0
* @category Array
* @param {Array} array The array to inspect.
* @param {Array|Function|Object|string} [iteratee=_.identity]
* The iteratee invoked per element.
* @returns {Array} Returns the new duplicate free array.
* @example
*
* _.uniqBy([2.1, 1.2, 2.3], Math.floor);
* // => [2.1, 1.2]
*
* // The `_.property` iteratee shorthand.
* _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
* // => [{ 'x': 1 }, { 'x': 2 }]
*/
回答by NSjonas
I expanded a bit on @IgorL solution, but extended prototype and gave it a selector function instead of a property to make it a little more flexible:
我对@IgorL 解决方案进行了一些扩展,但扩展了原型并为其提供了一个选择器函数而不是一个属性,以使其更加灵活:
Array.prototype.unique = function(selector) {
return this.filter((e, i) => this.findIndex((a) => {
if (selector) {
return selector(a) === selector(e);
}
return a === e;
}) === i);
};
Usage:
用法:
// with no param it uses strict equals (===) against the object
let primArr = ['one','one','two','three','one']
primArr.unique() // ['one','two','three']
let a = {foo:123}
let b = {foo:123}
let fooArr = [a,a,b]
fooArr.unique() //[a,b]
// alternatively, you can pass a selector function
fooArr.unique(item=>item.foo) //[{foo:123}] (first "unique" item returned)
Definitely NOT the most performant way to do this but as long as the selector is simple and the array isn't massive, it should work fine.
绝对不是执行此操作的最高效方法,但只要选择器简单且数组不是很大,它应该可以正常工作。
In Typescript
在打字稿中
Array.prototype.unique = function<T>(this: T[], selector?: (item: T) => object): T[] {
return this.filter((e, i) => this.findIndex((a) => {
if (selector) {
return selector(a) === selector(e);
}
return a === e;
}) === i);
};
回答by davidkonrad
My suggestion :
我的建议 :
Array.prototype.uniqueCity = function() {
var processed = [];
for (var i=this.length-1; i>=0; i--){
if (processed.indexOf(this[i].city)<0) {
processed.push(this[i].city);
} else {
this.splice(i, 1);
}
}
}
in use :
正在使用 :
places.uniqueCity();
or
或者
Array.prototype.uniqueObjectArray = function(field) {
var processed = [];
for (var i=this.length-1; i>=0; i--) {
if (this[i].hasOwnProperty(field)) {
if (processed.indexOf(this[i][field])<0) {
processed.push(this[i][field]);
} else {
this.splice(i, 1);
}
}
}
}
places.uniqueObjectArray('city');
With the above you can sort the array by any of the fields in the objects, even if they are not present for some of the objects.
使用上述内容,您可以按对象中的任何字段对数组进行排序,即使某些对象不存在它们。
or
或者
function uniqueCity(array) {
var processed = [];
for (var i=array.length-1; i>=0; i--){
if (processed.indexOf(array[i].city)<0) {
processed.push(array[i].city);
} else {
array.splice(i, 1);
}
}
return array;
}
places = uniqueCity(places);
回答by Tamo Maes
You could use a Map so the entries with the same key property (in your case 'city') only appear once
您可以使用 Map 以便具有相同键属性的条目(在您的情况下为“城市”)只出现一次
module.exports = (array, prop) => {
const keyValueArray = array.map(entry => [entry[prop], entry]);
const map = new Map(keyValueArray);
return Array.from(map.values());
};
More info about Map and array objects here
关于 Map 和 array 对象的更多信息在这里
回答by rafaelbiten
Another option:
另外一个选项:
const uniqueBy = prop => list => {
const uniques = {}
return list.reduce(
(result, item) => {
if (uniques[item[prop]]) return result
uniques[item[prop]] = item
return [...result, item]
},
[],
)
}
const uniqueById = uniqueBy('id')
uniqueById([
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
{ id: 1, name: 'one' },
{ id: 3, name: 'three' }
])
You can paste it on your console to see it working. It should work for the scenario presented and a few others.
您可以将其粘贴到您的控制台上以查看它的工作情况。它应该适用于呈现的场景和其他一些场景。
回答by Robert Byrne
As pointed out in the comments, you could use an object as a map, which will allow you to avoid duplicates, you can then enumerate the properties of the object.
正如评论中所指出的,您可以将对象用作映射,这将允许您避免重复,然后您可以枚举对象的属性。
working fiddle: http://jsfiddle.net/gPRPQ/1/
工作小提琴:http: //jsfiddle.net/gPRPQ/1/
var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";
places.push(a);
var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";
places.push(b);
var unique = {}
for (var i = 0; i < places.length; i++) {
var place = places[i];
unique[place.city] = place;
}
for (var name in unique) {
var place = unique[name];
console.log(place);
}
回答by pandian_Snkl
var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";
places.push(a);
var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";
places.push(b);
getUniqAR(places,'city'); //Return Uniq Array by property
function getUniqAR(Data,filter){
var uniar =[];
Data.forEach(function(item,ind,arr){
var dupi=false;
if(!uniar.length) uniar.push(item) //push first obj into uniq array
uniar.forEach(function(item2, ind2,arr){
if(item2[filter] == item[filter]){ //check each obj prop of uniq array
dupi=true; //if values are same put duplicate is true
}
})
if(!dupi){ uniar.push(item)} //if no duplicate insert to uniq
})
console.log(uniar)
return uniar;
}
回答by Shridhar Sagari
In simple Javascript
code to remove duplicate cities from places
array list is
Javascript
从places
数组列表中删除重复城市的简单代码是
var places = [{ 'lat': 12.123, 'lng': 13.213, 'city': "New York"},
{ 'lat': 3.123, 'lng': 2.213, 'city': "New York"},
{ 'lat': 43.123, 'lng': 12.213, 'city': "London"}];
var unique = [];
var tempArr = [];
places.forEach((value, index) => {
if (unique.indexOf(value.city) === -1) {
unique.push(value.city);
} else {
tempArr.push(index);
}
});
tempArr.reverse();
tempArr.forEach(ele => {
places.splice(ele, 1);
});
console.log(places);