Удалить дубликаты из массива объектов в JavaScript

375

У меня есть объект, который содержит массив объектов.

things = new Object();

things.thing = new Array();

things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});

Мне интересно, каков наилучший способ удалить дубликаты объектов из массива. Так, например, вещи. Все станет ...

{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}
Travis
источник
Вы имеете в виду, как остановить хеш-таблицу / объект с теми же параметрами, добавляемыми в массив?
Мэтью Лок
1
Mathew -> Если проще сначала запретить добавление дублирующегося объекта в массив, а не отфильтровывать его позже, да, это тоже подойдет.
Трэвис
2
Меня удивляет, как люди называют свои переменные. Иногда я думаю, что они действительно хотят сделать это излишне сложным. Далее посмотреть будет aaaaa.aaaa.push(...):)
дазито

Ответы:

154

Примитивный метод будет:

var obj = {};

for ( var i=0, len=things.thing.length; i < len; i++ )
    obj[things.thing[i]['place']] = things.thing[i];

things.thing = new Array();
for ( var key in obj )
    things.thing.push(obj[key]);
aefxx
источник
15
Вы никогда не должны использовать длину в цикле for, потому что это замедлит все, вычисляя ее на каждой итерации. Присвойте его переменной вне цикла и передайте переменную вместо things.thing.length.
0v3rth3d4wn
12
@aefxx Я не совсем понимаю эту функцию, как вы справляетесь с ситуацией, когда «место» одно и то же, но имя другое, следует ли считать dup или нет?
Куан
2
Хотя это работает, он не заботится о отсортированном массиве, поскольку выборка ключей никогда не гарантируется в порядке. Итак, вы в конечном итоге сортировать его снова. Теперь предположим, что массив не был отсортирован, но, тем не менее, его порядок важен, и вы не сможете гарантировать, что порядок останется без изменений
Deepak GM
1
@DeepakGM Ты абсолютно прав. Ответ не (обязательно) сохранит данный порядок. Если это требование, нужно искать другое решение.
Aefxx
Как я мог изменить вышеупомянутое, чтобы удалить объекты из массива, который содержит X так же как de-duped?
Райан Холтон
435

Как насчет es6магии?

things.thing = things.thing.filter((thing, index, self) =>
  index === self.findIndex((t) => (
    t.place === thing.place && t.name === thing.name
  ))
)

Ссылочный URL

Более общее решение будет:

const uniqueArray = things.thing.filter((thing, index) => {
  const _thing = JSON.stringify(thing);
  return index === things.thing.findIndex(obj => {
    return JSON.stringify(obj) === _thing;
  });
});

Пример Stackblitz

Eydrian
источник
81
Это может быть сокращено до:things.thing = things.thing.filter((thing, index, self) => self.findIndex(t => t.place === thing.place && t.name === thing.name) === index)
Джош Коул
Работает отлично! var uniqueArrayOfObjects = arrayOfObjects.filter (function (obj, index, self) {return index === self.findIndex (function (t) {return t ['obj-property'] === obj ['obj-property'] });}); Убедитесь, что вы используете правильный синтаксис JS.
Мохамед Салем Ламири
Это правильный синтаксис JS. Ваш не использует 1) функции жирной стрелки 2) неявный возврат или 3) точечную запись. Ваш синтаксис ES5. Другие в основном ES6 (ECMA2015). Все действительны в 2017 году. См. Комментарий Джаредвилли.
agm1984
8
@vsync, просто возьмите ответ @ BKM и соедините его, общее решение будет следующим: const uniqueArray = arrayOfObjects.filter((object,index) => index === arrayOfObjects.findIndex(obj => JSON.stringify(obj) === JSON.stringify(object))); jsfiddle.net/x9ku0p7L/28
Эйдриан
9
Ключевым моментом здесь является то, что метод findIndex () возвращает индекс первого элемента, поэтому, если найдется второй соответствующий элемент, он никогда не будет найден и добавлен во время фильтра. Я смотрел на это в течение минуты :)
JBaczuk
111

Если вы можете использовать библиотеки Javascript, такие как подчеркивание или lodash, я рекомендую взглянуть на _.uniqфункцию в их библиотеках. От lodash:

_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])

По сути, вы передаете массив, который здесь является литералом объекта, и передаете атрибут, с которым вы хотите удалить дубликаты, в исходном массиве данных, например так:

var data = [{'name': 'Amir', 'surname': 'Rahnama'}, {'name': 'Amir', 'surname': 'Stevens'}];
var non_duplidated_data = _.uniq(data, 'name'); 

ОБНОВЛЕНИЕ : Lodash теперь представил .uniqByтакже.

ambodi
источник
3
@Praveen Pds: Я говорил что-нибудь о подчеркивании в примере кода? Я сказал, что у 'lodash' есть эта функция, и у подчеркивания есть подобные. Прежде чем голосовать, пожалуйста, внимательно прочитайте ответы.
Амбоди
// Вывод списка уникальных объектов с использованием _underscore.js holdingObject = _.uniq (holdingObject, function (item, key, name) {return item.name;});
praveenpds
26
Примечание: теперь вам нужно использовать uniqByвместо uniq, например,_.uniqBy(data, 'name') ... документацию: lodash.com/docs#uniqBy
drmrbrewer
83

У меня было точно такое же требование, чтобы удалить дубликаты объектов в массиве на основе дубликатов в одном поле. Я нашел код здесь: Javascript: удаление дубликатов из массива объектов

Поэтому в моем примере я удаляю любой объект из массива, который имеет дублирующееся строковое значение licenseNum.

var arrayWithDuplicates = [
    {"type":"LICENSE", "licenseNum": "12345", state:"NV"},
    {"type":"LICENSE", "licenseNum": "A7846", state:"CA"},
    {"type":"LICENSE", "licenseNum": "12345", state:"OR"},
    {"type":"LICENSE", "licenseNum": "10849", state:"CA"},
    {"type":"LICENSE", "licenseNum": "B7037", state:"WA"},
    {"type":"LICENSE", "licenseNum": "12345", state:"NM"}
];

function removeDuplicates(originalArray, prop) {
     var newArray = [];
     var lookupObject  = {};

     for(var i in originalArray) {
        lookupObject[originalArray[i][prop]] = originalArray[i];
     }

     for(i in lookupObject) {
         newArray.push(lookupObject[i]);
     }
      return newArray;
 }

var uniqueArray = removeDuplicates(arrayWithDuplicates, "licenseNum");
console.log("uniqueArray is: " + JSON.stringify(uniqueArray));

Результаты:

uniqueArray - это:

[{"type":"LICENSE","licenseNum":"10849","state":"CA"},
{"type":"LICENSE","licenseNum":"12345","state":"NM"},
{"type":"LICENSE","licenseNum":"A7846","state":"CA"},
{"type":"LICENSE","licenseNum":"B7037","state":"WA"}]
Джеймс Дринкард
источник
1
Это было бы более полезно, если бы функция могла также фильтровать ложные объекты. for(var i in array) { if(array[i][prop]){ //valid lookupObject[array[i][prop]] = array[i]; } else { console.log('falsy object'); } }
Абдул Садик Ялчин
Почему бы не снизить сложность 0 (n), используя: for (let i in originalArray) { if (lookupObject[originalArray[i]['id']] === undefined) { newArray.push(originalArray[i]); } lookupObject[originalArray[i]['id']] = originalArray[i]; }
Tudor B.
это лучший способ, потому что важно знать, что вы хотите, чтобы вас не дублировали. Теперь это можно сделать через редуктор для стандартов е6?
Кристиан Мэтью
70

Самый короткий лайнер для ES6 +

Найти уникальные idв массиве.

arr.filter((v,i,a)=>a.findIndex(t=>(t.id === v.id))===i)

Уникальный по нескольким свойствам ( placeи name)

arr.filter((v,i,a)=>a.findIndex(t=>(t.place === v.place && t.name===v.name))===i)

Уникальный по всем свойствам (это будет медленно для больших массивов)

arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i)

Сохраните последнее вхождение.

arr.slice().reverse().filter((v,i,a)=>a.findIndex(t=>(t.id === v.id))===i).reverse()
цыплята
источник
2
Магия, это верный ответ
Луис Контрерас
48

Один лайнер, используя Set

var things = new Object();

things.thing = new Array();

things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});

// assign things.thing to myData for brevity
var myData = things.thing;

things.thing = Array.from(new Set(myData.map(JSON.stringify))).map(JSON.parse);

console.log(things.thing)

Объяснение:

  1. new Set(myData.map(JSON.stringify))создает набор объект используя строковые элементы myData.
  2. Установка объекта гарантирует, что каждый элемент уникален.
  3. Затем я создаю массив на основе элементов созданного набора, используя Array.from.
  4. Наконец, я использую JSON.parse, чтобы преобразовать строковый элемент обратно в объект.
Mμ.
источник
18
проблема в том, что {a: 1, b: 2} не будет равно {b: 2, a: 1}
PirateApp
2
имейте в виду, что могут быть проблемы со свойствами Date
MarkosyanArtur
Эта строка создает случайные нулевые значения с объектом строки, которые не существуют в исходном массиве объектов. Можете ли вы помочь?
B1K
48

Используя ES6 + в одной строке, вы можете получить уникальный список объектов по ключу:

const unique = [...new Map(arr.map(item => [item[key], item])).values()]

Это может быть помещено в функцию:

function getUniqueListBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()]
}

Вот рабочий пример:

const arr = [
    {place: "here",  name: "x", other: "other stuff1" },
    {place: "there", name: "x", other: "other stuff2" },
    {place: "here",  name: "y", other: "other stuff4" },
    {place: "here",  name: "z", other: "other stuff5" }
]

function getUniqueListBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()]
}

const arr1 = getUniqueListBy(arr, 'place')

console.log("Unique by place")
console.log(JSON.stringify(arr1))

console.log("\nUnique by name")
const arr2 = getUniqueListBy(arr, 'name')

console.log(JSON.stringify(arr2))

Как это работает

Сначала массив переопределяется таким образом, чтобы его можно было использовать в качестве входных данных для карты.

arr.map (item => [item [key], item]);

что означает, что каждый элемент массива будет преобразован в другой массив с 2 элементами; выбранный ключ в качестве первого элемента и весь исходного элемента в качестве второго элемента, это называется запись (напр. , запись массива , Записи на карте ). А вот официальный документ с примером, показывающим, как добавить записи массива в конструкторе Map.

Пример , когда ключ место :

[["here", {place: "here",  name: "x", other: "other stuff1" }], ...]

Во-вторых, мы передаем этот модифицированный массив конструктору Map, и здесь происходит волшебство. Карта удалит дублирующиеся значения ключей, сохраняя только последнее вставленное значение того же ключа. Примечание . Карта сохраняет порядок вставки. ( проверьте разницу между картой и объектом )

новая карта (входной массив только что отображен выше)

В-третьих, мы используем значения карты для извлечения оригинальных элементов, но на этот раз без дубликатов.

новая карта (mappedArr) .values ​​()

И последнее - добавить эти значения в новый новый массив, чтобы он мог выглядеть как исходная структура и вернуть это:

return [... new Map (mappedArr) .values ​​()]

В. Самбор
источник
Это не отвечает на первоначальный вопрос, так как это поиск id. Вопрос требует, чтобы весь объект был уникальным во всех областях, таких какplace иname
Л. Холанда
Ваша функция ES6 кажется очень лаконичной и практичной. Можете ли вы объяснить это немного больше? Что именно происходит? Первые или последние дубликаты удалены? Или это случайно, какой дубликат удаляется? Это было бы полезно, спасибо.
Дэвид Шуман
Насколько я могу судить, карта со значением свойства в качестве ключа создается. Но это не 100%, как или если порядок массива сохраняется.
Дэвид Шуман
1
Привет @DavidSchumann, я обновлю ответ и объясню, как это работает. Но для краткого ответа порядок сохраняется, а первый удаляется ... Подумайте, как он вставлен в карту ... Он проверяет, существует ли ключ, он обновит его, поэтому последний останется
V Самбор
30

Вот еще один вариант сделать это с помощью итерационных методов Array, если вам нужно сравнение только по одному полю объекта:

    function uniq(a, param){
        return a.filter(function(item, pos, array){
            return array.map(function(mapItem){ return mapItem[param]; }).indexOf(item[param]) === pos;
        })
    }

    uniq(things.thing, 'place');
Алекс Кобылинский
источник
Хотя этот порядок больше, чем O (n²), он подходит для моего варианта использования, поскольку размер моего массива всегда будет меньше 30. Спасибо!
Sterex 13.07.16
24

один лайнер здесь

let arr = [
  {id:1,name:"sravan ganji"},
  {id:2,name:"anu"},
  {id:4,name:"mammu"},
  {id:3,name:"sanju"},
  {id:3,name:"ram"},
];

console.log(Object.values(arr.reduce((acc,cur)=>Object.assign(acc,{[cur.id]:cur}),{})))

шраван ганджи
источник
1
Красиво и чисто, если вы хотите удалить объекты только с одним дублирующим значением, не так чисто для полностью дублированных объектов.
Дэвид Баркер
22

Если вы можете ждать устранения дубликатов до тех пор, пока не завершатся все добавления, типичный подход состоит в том, чтобы сначала отсортировать массив, а затем удалить дубликаты. Сортировка избегает подхода N * N сканирования массива для каждого элемента, когда вы проходите через них.

Функция «устранить дубликаты» обычно называется уникальной или уникальной . Некоторые существующие реализации могут объединять два этапа, например, uniq прототипа

В этом посте есть несколько идей, которые можно попробовать (и некоторые, которых следует избегать :-)), если в вашей библиотеке их еще нет ! Лично я считаю это самым прямым:

    function unique(a){
        a.sort();
        for(var i = 1; i < a.length; ){
            if(a[i-1] == a[i]){
                a.splice(i, 1);
            } else {
                i++;
            }
        }
        return a;
    }  

    // Provide your own comparison
    function unique(a, compareFunc){
        a.sort( compareFunc );
        for(var i = 1; i < a.length; ){
            if( compareFunc(a[i-1], a[i]) === 0){
                a.splice(i, 1);
            } else {
                i++;
            }
        }
        return a;
    }
maccullt
источник
Это не будет работать для универсальных объектов без естественного порядка сортировки.
Тим Даун
Правда, я добавил предоставленную пользователем версию для сравнения.
Maccullt
Ваша предоставленная пользователем версия сравнения не будет работать, потому что, если ваша функция сравнения - function(_a,_b){return _a.a===_b.a && _a.b===_b.b;}то массив не будет отсортирован.
graham.reeds
1
Это недопустимая функция сравнения. От developer.mozilla.org/en/Core_JavaScript_1.5_Reference/… ... функция сравнения (a, b) {если (a по некоторому критерию порядка меньше b) return -1; если (a больше, чем b по критерию упорядочения), вернуть 1; // a должно быть равно b return 0; } ...
maccullt
22

Самый простой способ это использовать filter:

var uniq = {}
var arr  = [{"id":"1"},{"id":"1"},{"id":"2"}]
var arrFiltered = arr.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
console.log('arrFiltered', arrFiltered)

Алекс Дики
источник
6
Хорошей практикой в ​​Stack Overflow является добавление объяснения того, почему ваше решение должно работать, особенно то, как ваше решение лучше, чем другие ответы. Для получения дополнительной информации прочитайте, как ответить .
Сэмюэль Лью
Это не отвечает на первоначальный вопрос, так как это поиск id. Вопрос требует, чтобы весь объект был уникальным во всех областях, таких как placeиname
Л. Холанда
17

Это общий способ сделать это: вы передаете функцию, которая проверяет, считаются ли два элемента массива равными. В этом случае сравниваются значения nameи placeсвойства двух сравниваемых объектов.

ES5 ответ

function removeDuplicates(arr, equals) {
    var originalArr = arr.slice(0);
    var i, len, val;
    arr.length = 0;

    for (i = 0, len = originalArr.length; i < len; ++i) {
        val = originalArr[i];
        if (!arr.some(function(item) { return equals(item, val); })) {
            arr.push(val);
        }
    }
}

function thingsEqual(thing1, thing2) {
    return thing1.place === thing2.place
        && thing1.name === thing2.name;
}

var things = [
  {place:"here",name:"stuff"},
  {place:"there",name:"morestuff"},
  {place:"there",name:"morestuff"}
];

removeDuplicates(things, thingsEqual);
console.log(things);

Оригинальный ответ ES3

function arrayContains(arr, val, equals) {
    var i = arr.length;
    while (i--) {
        if ( equals(arr[i], val) ) {
            return true;
        }
    }
    return false;
}

function removeDuplicates(arr, equals) {
    var originalArr = arr.slice(0);
    var i, len, j, val;
    arr.length = 0;

    for (i = 0, len = originalArr.length; i < len; ++i) {
        val = originalArr[i];
        if (!arrayContains(arr, val, equals)) {
            arr.push(val);
        }
    }
}

function thingsEqual(thing1, thing2) {
    return thing1.place === thing2.place
        && thing1.name === thing2.name;
}

removeDuplicates(things.thing, thingsEqual);
Тим Даун
источник
1
Два объекта не будут равны, даже если они имеют одинаковые свойства и значения.
Кеннебек
Да, я знаю. Но, честно говоря, я не смог правильно прочитать вопрос: я не заметил, что это объекты с идентичными свойствами, которые ему необходимо отсеять. Я отредактирую свой ответ.
Тим Даун
1
вместо while внутри arrayContains - использовать метод Array.prototype..some Возвращает true, если один из членов массива соответствует условию
MarkosyanArtur
13

Добавить еще один в список. Использование ES6 и Array.reduceс Array.find.
В этом примере выполняется фильтрация объектов на основе guidсвойства.

let filtered = array.reduce((accumulator, current) => {
  if (! accumulator.find(({guid}) => guid === current.guid)) {
    accumulator.push(current);
  }
  return accumulator;
}, []);

Расширение этого, чтобы позволить выбор свойства и сжать его в один лайнер:

const uniqify = (array, key) => array.reduce((prev, curr) => prev.find(a => a[key] === curr[key]) ? prev : prev.push(curr) && prev, []);

Чтобы использовать его, передайте массив объектов и имя ключа, для которого вы хотите дедуплицировать, в виде строкового значения:

const result = uniqify(myArrayOfObjects, 'guid')
Пит Б
источник
11

Вы также можете использовать Map:

const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());

Полный образец:

const things = new Object();

things.thing = new Array();

things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});

const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());

console.log(JSON.stringify(dedupThings, null, 4));

Результат:

[
    {
        "place": "here",
        "name": "stuff"
    },
    {
        "place": "there",
        "name": "morestuff"
    }
]
Pragmateek
источник
+1, хорошо, если объяснить немного больше о внутренней работе dedup, все будет хорошо - с другой стороны, теперь я понимаю, что уменьшите: D
MimiEAM
11

Черт, дети, давайте раздавим эту штуку, почему бы и нет?

let uniqIds = {}, source = [{id:'a'},{id:'b'},{id:'c'},{id:'b'},{id:'a'},{id:'d'}];
let filtered = source.filter(obj => !uniqIds[obj.id] && (uniqIds[obj.id] = true));
console.log(filtered);
// EXPECTED: [{id:'a'},{id:'b'},{id:'c'},{id:'d'}];

Клифф Холл
источник
Это не отвечает на первоначальный вопрос, так как это поиск id. Вопрос требует, чтобы весь объект был уникальным во всех областях, таких как placeиname
Л. Холанда
Это уточнение приведенного выше обобщения проблемы. Оригинальный вопрос был опубликован 9 лет назад, поэтому, возможно, оригинальный плакат не беспокоит placeи nameсегодня. Любой, кто читает эту ветку, ищет оптимальный способ дедупликации списка объектов, и это компактный способ сделать это.
Клифф Холл
11

Решение TypeScript

Это удалит дубликаты объектов, а также сохранит типы объектов.

function removeDuplicateObjects(array: any[]) {
  return [...new Set(array.map(s => JSON.stringify(s)))]
    .map(s => JSON.parse(s));
}

источник
2
это здорово и коротко!
Мохж,
И очень медленный тоже ...
Л. Холанда
7

принимая во внимание lodash.uniqWith

var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];

_.uniqWith(objects, _.isEqual);
// => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
Джастин
источник
Отлично ! Спасибо ;-)
DonFabiolas
1
Ни Unik, ни Unik lodash не сработали, но ваше решение сработало. Спасибо! Пожалуйста, дайте источник вашего кода, однако, если это прямая копия. lodash.com/docs/4.17.10#uniqWith
Ману Си Джей
5

Другой вариант - создать пользовательскую функцию indexOf, которая сравнивает значения выбранного вами свойства для каждого объекта и заключает его в функцию сокращения.

var uniq = redundant_array.reduce(function(a,b){
      function indexOfProperty (a, b){
          for (var i=0;i<a.length;i++){
              if(a[i].property == b.property){
                   return i;
               }
          }
         return -1;
      }

      if (indexOfProperty(a,b) < 0 ) a.push(b);
        return a;
    },[]);
ZeroSum
источник
это отлично сработало для меня - я связал это с lodash.isequalпакетом npm в качестве облегченного компаратора объектов для выполнения уникальной фильтрации массива ... например, отдельного массива объектов. Просто поменялся местами if (_.isEqual(a[i], b)) {вместо того, чтобы искать @ единственное свойство
SliverNinja - MSFT
5

let myData = [{place:"here",name:"stuff"}, 
 {place:"there",name:"morestuff"},
 {place:"there",name:"morestuff"}];


let q = [...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];

console.log(q)

Однострочник с использованием ES6 и new Map().

// assign things.thing to myData
let myData = things.thing;

[...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];

Подробности:-

  1. Выполнение .map()в списке данных и преобразование каждого отдельного объекта в [key, value]массив пар (длина = 2), первый элемент (ключ) будет stringifiedверсия объекта, а второй (значение) будетobject сам по себе.
  2. Добавление списка созданных выше массивов к new Map()ключу будет иметь stringifiedобъект как объект, и любое добавление того же ключа приведет к переопределению уже существующего ключа.
  3. Использование .values()даст MapIterator со всеми значениями в карте (obj в нашем случае)
  4. Наконец, spread ...оператор, чтобы дать новый массив со значениями из предыдущего шага.
Саван Акбари
источник
4

Вот решение для es6, где вы хотите сохранить только последний элемент. Это решение является функциональным и соответствует стилю Airbnb.

const things = {
  thing: [
    { place: 'here', name: 'stuff' },
    { place: 'there', name: 'morestuff1' },
    { place: 'there', name: 'morestuff2' }, 
  ],
};

const removeDuplicates = (array, key) => {
  return array.reduce((arr, item) => {
    const removed = arr.filter(i => i[key] !== item[key]);
    return [...removed, item];
  }, []);
};

console.log(removeDuplicates(things.thing, 'place'));
// > [{ place: 'here', name: 'stuff' }, { place: 'there', name: 'morestuff2' }]
Михей
источник
Вы можете удалить дубликат, а также удалить этот дубликат с помощью этого кода. Ницца
sg28
4

removeDuplicates () принимает массив объектов и возвращает новый массив без каких-либо дубликатов объектов (на основе свойства id).

const allTests = [
  {name: 'Test1', id: '1'}, 
  {name: 'Test3', id: '3'},
  {name: 'Test2', id: '2'},
  {name: 'Test2', id: '2'},
  {name: 'Test3', id: '3'}
];

function removeDuplicates(array) {
  let uniq = {};
  return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true))
}

removeDuplicates(allTests);

Ожидаемый результат:

[
  {name: 'Test1', id: '1'}, 
  {name: 'Test3', id: '3'},
  {name: 'Test2', id: '2'}
];

Сначала мы устанавливаем значение переменной uniq в пустой объект.

Далее мы фильтруем массив объектов. Фильтр создает новый массив со всеми элементами, которые проходят тест, реализованный предоставленной функцией.

return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));

Выше мы используем функцию короткого замыкания &&. Если левая часть && имеет значение true, тогда она возвращает значение справа от &&. Если левая сторона ложна, она возвращает то, что находится слева от &&.

Для каждого объекта (obj) мы проверяем uniq для свойства, называемого значением obj.id (в этом случае на первой итерации оно проверяет свойство '1'.) Мы хотим противоположность того, что возвращает (либо true или ложь) именно поэтому мы используем! в! uniq [obj.id]. Если uniq уже имеет свойство id, он возвращает значение true, которое оценивается как ложное (!), Говорящее функции фильтра НЕ добавлять этот объект. Однако, если он не находит свойство obj.id, он возвращает значение false, которое затем оценивается как true (!) И возвращает все справа от && или (uniq [obj.id] = true). Это истинное значение, указывающее методу фильтра добавить obj к возвращаемому массиву, а также добавляет свойство {1: true} в uniq. Это гарантирует, что любой другой экземпляр obj с тем же идентификатором не будет добавлен снова.

MarkN
источник
Возможно, объясните свой код, и как он отвечает на вопрос?
mix3d
Спасибо, mix3d. Я добавил уточнение.
MarkN
Спасибо за объяснение этого! Это решение работает для меня, и похоже на пару других, опубликованных здесь, хотя я не понимаю, что происходит :)
Тим Моллой
3
let data = [
  {
    'name': 'Amir',
    'surname': 'Rahnama'
  }, 
  {
    'name': 'Amir',
    'surname': 'Stevens'
  }
];
let non_duplicated_data = _.uniqBy(data, 'name');
qzttt
источник
9
Пожалуйста, добавьте пояснения к своему коду, чтобы будущие посетители могли понять, что вы делаете. Спасибо.
Ошибки
Ваш ответ зависит от внешней библиотеки кода ...
Тейлор А. Лич
3

Я считаю, что сочетание reduceс, JSON.stringifyчтобы идеально сравнить объекты и выборочное добавление тех, кто еще не находится в аккумуляторе, является элегантным способом.

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

var collection= [{a:1},{a:2},{a:1},{a:3}]

var filtered = collection.reduce((filtered, item) => {
  if( !filtered.some(filteredItem => JSON.stringify(filteredItem) == JSON.stringify(item)) )
    filtered.push(item)
  return filtered
}, [])

console.log(filtered)

Другой способ написать то же самое (но менее эффективно):

collection.reduce((filtered, item) => 
  filtered.some(filteredItem => 
    JSON.stringify(filteredItem ) == JSON.stringify(item)) 
      ? filtered
      : [...filtered, item]
, [])
VSync
источник
тот, который работает для меня! благодарю вас!
javascript110899
2

Продолжаем исследовать способы удаления дубликатов из массива объектов в ES6: установка thisArgаргумента Array.prototype.filterto new Setобеспечивает достойную альтернативу:

const things = [
  {place:"here",name:"stuff"},
  {place:"there",name:"morestuff"},
  {place:"there",name:"morestuff"}
];

const filtered = things.filter(function({place, name}) {

  const key =`${place}${name}`;

  return !this.has(key) && this.add(key);

}, new Set);

console.log(filtered);

Однако он не будет работать с функциями стрелок () =>, поскольку thisсвязан с их лексической областью действия.

Леонид Пирлия
источник
2

ES6 магия в одну строку ... читается при этом!

// returns the union of two arrays where duplicate objects with the same 'prop' are removed
const removeDuplicatesWith = (a, b, prop) => a.filter(x => !b.find(y => x[prop] === y[prop]);
Иосия Коад
источник
2

Простое решение с помощью ES6 вспомогательных методов «уменьшить» и «найти» массива

Работает качественно и отлично отлично!

"use strict";

var things = new Object();
things.thing = new Array();
things.thing.push({
    place: "here",
    name: "stuff"
});
things.thing.push({
    place: "there",
    name: "morestuff"
});
things.thing.push({
    place: "there",
    name: "morestuff"
});

// the logic is here

function removeDup(something) {
    return something.thing.reduce(function (prev, ele) {
        var found = prev.find(function (fele) {
            return ele.place === fele.place && ele.name === fele.name;
        });
        if (!found) {
            prev.push(ele);
        }
        return prev;
    }, []);
}
console.log(removeDup(things));
К.Дж. Сударшан
источник
Это мне очень помогло, спасибо
jpisty
1

Если вы не возражаете против сортировки вашего уникального массива впоследствии, это будет эффективным решением:

things.thing
  .sort(((a, b) => a.place < b.place)
  .filter((current, index, array) =>
    index === 0 || current.place !== array[index - 1].place)

Таким образом, вам нужно только сравнить текущий элемент с предыдущим элементом в массиве. Сортировка один раз перед filtering ( O(n*log(n))) дешевле, чем поиск дубликата во всем массиве для каждого элемента массива ( O(n²)).

Клеменс Хелм
источник
1

Это простой способ, как убрать двуличие из массива объектов.

Я много работаю с данными, и это полезно для меня.

const data = [{name: 'AAA'}, {name: 'AAA'}, {name: 'BBB'}, {name: 'AAA'}];
function removeDuplicity(datas){
    return datas.filter((item, index,arr)=>{
    const c = arr.map(item=> item.name);
    return  index === c.indexOf(item.name)
  })
}

console.log(removeDuplicity(data))

выведет на консоль:

[[object Object] {
name: "AAA"
}, [object Object] {
name: "BBB"
}]
Юрай
источник
Это решение предназначено для удаления дублирования из статического массива, но когда вы помещаете данные из бэкэнда в массив данных, тогда подумайте об использовании замены. Потому что в этом случае новое значение, помещенное в массив данных, будет удалено, а «старое» значение все равно будет сохранено в массиве данных.
Юрай
1
str =[
{"item_id":1},
{"item_id":2},
{"item_id":2}
]

obj =[]
for (x in str){
    if(check(str[x].item_id)){
        obj.push(str[x])
    }   
}
function check(id){
    flag=0
    for (y in obj){
        if(obj[y].item_id === id){
            flag =1
        }
    }
    if(flag ==0) return true
    else return false

}
console.log(obj)

str это массив объектов. Существуют объекты, имеющие одинаковое значение (здесь небольшой пример, есть два объекта с одинаковым item_id, равным 2). check (id) - это функция, которая проверяет, существует ли какой-либо объект с таким же item_id или нет. если он существует, вернуть false, в противном случае вернуть true. В соответствии с этим результатом поместите объект в новый массив obj . Вывод приведенного выше кода [{"item_id":1},{"item_id":2}]

Бибин Хаймон
источник
Добавить описание
Mathews Sunny
@Billa Это нормально?
Бибин Хаймон
1

Вы слышали о библиотеке Лодаш? Я рекомендую вам эту утилиту, когда вы действительно не хотите применять свою логику к коду и использовать уже существующий код, который оптимизирован и надежен.

Подумайте о создании такого массива

things.thing.push({place:"utopia",name:"unicorn"});
things.thing.push({place:"jade_palace",name:"po"});
things.thing.push({place:"jade_palace",name:"tigress"});
things.thing.push({place:"utopia",name:"flying_reindeer"});
things.thing.push({place:"panda_village",name:"po"});

Обратите внимание, что если вы хотите сохранить один атрибут уникальным, вы можете сделать это с помощью библиотеки lodash. Здесь вы можете использовать _.uniqBy

.uniqBy (массив, [iteratee = .identity])

Этот метод похож на _.uniq (который возвращает версию массива без дубликатов, в которой сохраняется только первое вхождение каждого элемента), за исключением того, что он принимает iteratee, который вызывается для каждого элемента в массиве, чтобы сгенерировать критерий, по которому уникальность вычисляется.

Так, например, если вы хотите вернуть массив, имеющий уникальный атрибут 'place'

_.uniqBy (things.thing, 'place')

Точно так же, если вы хотите уникальный атрибут как «имя»

_.uniqBy (things.thing, 'name')

Надеюсь это поможет.

Ура!

Mayank Gangwal
источник