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

Underscore/lodash уникальный по нескольким свойствам

У меня есть массив объектов с дубликатами, и я пытаюсь получить уникальный список, где уникальность определяется подмножеством свойств объекта. Например,

{a:"1",b:"1",c:"2"}

И я хочу игнорировать c в сравнении уникальности.

Я могу сделать что-то вроде

_.uniq(myArray,function(element) { return element.a + "_" + element+b});

Я надеялся, что смогу сделать

_.uniq(myArray,function(element) { return {a:element.a, b:element.b} });

Но это не работает. Есть ли что-то подобное, что я могу сделать, или мне нужно создать сопоставимое представление объекта, если я сравниваю несколько свойств?

4b9b3361

Ответ 1

Похоже, что нет простого способа сделать это, к сожалению. Если вы не написали свою собственную функцию для этого, вам нужно будет вернуть что-то, что можно напрямую сравнить для равенства (как в первом примере).

Один из методов - просто .join() необходимые вам свойства:

_.uniqBy(myArray, function(elem) { return [elem.a, elem.b].join(); });

В качестве альтернативы вы можете использовать _.pick или _.omit, чтобы удалить все, что вы не делаете необходимость. Оттуда вы можете использовать _.values с .join() или даже просто JSON.stringify:

_.uniqBy(myArray, function(elem) {
    return JSON.stringify(_.pick(elem, ['a', 'b']));
});

Имейте в виду, что объекты не детерминированы, поскольку порядок свойств идет, поэтому вы можете просто придерживаться метода явного массива.

P.S. Замените uniqBy на uniq для Lodash < 4

Ответ 2

Я действительно думаю, что метод join() по-прежнему является самым простым. Несмотря на проблемы, поднятые в предыдущем решении, я думаю, что выбор правильного разделителя является ключом к предотвращению выявленных ошибок (с разными наборами значений, возвращающими одно и то же значение). Имейте в виду, что разделитель не должен быть единственным символом, это может быть любая строка, которая, как вы уверены, не будет происходить естественным образом в самих данных. Я делаю это все время и люблю использовать '~! $~' В качестве моего разделителя. Он также может включать специальные символы, такие как \t\r\n и т.д.

Если содержащиеся данные действительно непредсказуемы, возможно, максимальная длина известна, и вы можете просто проложить каждый элемент до максимальной длины до присоединения.

Ответ 3

Используйте метод Lodash uniqWith:

_.uniqWith(array, [comparator])

Этот метод похож на _.uniq за исключением того, что он принимает comparator который вызывается для сравнения элементов array. Порядок значений результатов определяется порядком их появления в массиве. Компаратор вызывается с двумя аргументами: (arrVal, othVal).

Когда comparator возвращает true, элементы считаются дубликатами, и только новое вхождение будет включено в новый массив.


Пример:
У меня есть список местоположений с latitude и longitude - некоторые из которых идентичны - и я хочу увидеть список уникальных мест:

const locations = [
  {
    name: "Office 1",
    latitude: -30,
    longitude: -30
  },
  {
    name: "Office 2",
    latitude: -30,
    longitude: 10
  },
  {
    name: "Office 3",
    latitude: -30,
    longitude: 10
  }
];

const uniqueLocations = _.uniqWith(
  locations,
  (locationA, locationB) =>
    locationA.latitude === locationB.latitude &&
    locationA.longitude === locationB.longitude
);

// Result has Office 1 and Office 2

Ответ 4

В совмещенном ответе @voithos и @Danail есть подсказка. Я решил, как это добавить уникальный ключ к объектам в моем массиве.

Начальные данные образца

const animalArray = [
  { a: 4, b: 'cat', d: 'generic' },
  { a: 5, b: 'cat', d: 'generic' },
  { a: 4, b: 'dog', d: 'generic' },
  { a: 4, b: 'cat', d: 'generic' },
];

В приведенном выше примере я хочу, чтобы массив был уникальным для a и b но сейчас у меня есть два объекта, которые имеют a: 4 и b: 'cat'. Объединяя + b в строку, я могу получить уникальный ключ для проверки.

{ a: 4, b: 'cat', d: 'generic', id: '${a}-${b}' }. // id is now '4-cat'

Примечание. Очевидно, что вам необходимо отобразить данные или сделать это при создании объекта, поскольку вы не можете ссылаться на свойства объекта в пределах одного и того же объекта.

Теперь сравнение простое...

_.uniqBy(animalArray, 'id');

Полученный массив будет длиной 3, в нем будет удален последний дубликат.