Подтвердить что ты не робот

Создание массива уникальных объектов по свойству

Я создал массив таких объектов:

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 для достижения этой цели?

4b9b3361

Ответ 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;
}

Вы можете просмотреть скрипку здесь