Создание массива уникальных объектов по свойству
Я создал массив таких объектов:
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);
...
Я пытаюсь создать новый массив, который фильтрует места, чтобы содержать только объекты, которые не имеют одного и того же свойства города (дубликаты lat/lng в порядке). Есть ли встроенная функция JS или JQuery для достижения этой цели?
Ответы
Ответ 1
Я бы использовал объект флагов во время фильтрации, например:
var flags = {};
var newPlaces = places.filter(function(entry) {
if (flags[entry.city]) {
return false;
}
flags[entry.city] = true;
return true;
});
Это использует Array#filter
из ECMAScript5 (ES5), который является одним из дополнений ES5, которые можно открепить (поиск "es5 shim" для нескольких вариантов).
Вы можете сделать это без 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);
}
});
Оба вышеуказанного предполагают, что первый объект с данным городом должен быть сохранен, а все остальные отброшены.
Примечание. Как указано user2736012, мой тест if (flags[entry.city])
будет прав для городов с именами, которые бывают такими же, как свойства, которые существуют на Object.prototype
, например toString
. В этом случае очень маловероятно, но есть четыре способа избежать этой возможности:
-
(Мое обычное предпочтительное решение) Создайте объект без прототипа: 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, вместо этого вы можете использовать Set
:
const flags = new Set();
const newPlaces = places.filter(entry => {
if (flags.has(entry.city)) {
return false;
}
flags.add(entry.city);
return true;
});
Ответ 2
Самое короткое , но не лучшее решение (см. Обновление ниже) для es6:
function unique(array, propertyName) {
return array.filter((e, i) => array.findIndex(a => a[propertyName] === e[propertyName]) === i);
}
производительность: https://jsperf.com/compare-unique-array-by-property
Ответ 3
Мое предложение:
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);
}
}
}
в использовании:
places.uniqueCity();
или
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');
С вышесказанным вы можете отсортировать массив по любому из полей в объектах, даже если они отсутствуют для некоторых объектов.
или
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);
Ответ 4
https://lodash.com/docs#uniqBy
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 }]
*/
Ответ 5
Я немного расширил решение @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);
};
Использование:
// 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)
Определенно НЕ самый эффективный способ сделать это, но пока селектор прост, а массив не массивен, он должен работать нормально.
В машинописи
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);
};
Ответ 6
Как указано в комментариях, вы можете использовать объект как карту, что позволит избежать дубликатов, затем вы можете перечислить свойства объекта.
рабочая скрипка: 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);
}
Ответ 7
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;
}
Ответ 8
Другой вариант:
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' }
])
Вы можете вставить его на консоль, чтобы увидеть, как он работает. Это должно работать для представленного сценария и нескольких других.
Ответ 9
Вы можете использовать карту, чтобы записи с таким же ключевым свойством (в вашем случае 'city') появлялись только один раз.
module.exports = (array, prop) => {
const keyValueArray = array.map(entry => [entry[prop], entry]);
const map = new Map(keyValueArray);
return Array.from(map.values());
};
Подробнее о карте и объектах массива здесь
Базовый пример на Codepen
Ответ 10
В простом коде 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);
Ответ 11
Эта ветка может быть старой, но я подумал, что должен поделиться ею. Он основан на Pure JavaScript и удаляет Duplicate Objects на основе указанных свойств.
function removeDuplicates(originalArray, properties) {
var newArray = [];
var index = 0;
var lookupObject = {};
var totalProperties = properties.length;
for (var i = 0; i < originalArray.length; i++) {
var exists = false;
for (var a = 0; a < newArray.length; a++) {
var propsFound = 0;
for (var b = 0; b < totalProperties; b++) {
if (originalArray[i][properties[b]] == newArray[a][properties[b]]) {
propsFound++;
}
}
//If there is a match then break the for loop
if (propsFound == totalProperties) {
exists = true;
break;
}
} //End of New Array
if (!exists) {
newArray[index] = originalArray[i];
index++;
}
} //End of originalArray
return newArray;
}
Вы можете просмотреть скрипку здесь