Видаліть дублікати з масиву об’єктів у JavaScript


373

У мене є об’єкт, який містить масив об'єктів.

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"});

Мені цікаво, який найкращий спосіб видалити повторювані об'єкти з масиву. Так, наприклад, things.thing стане ...

{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}

Ви маєте на увазі, як ви зупиняєте хеш-файл / об'єкт, коли всі масиви додаються до масиву?
Метью Лок

1
Mathew -> Якщо простіше запобігти додаванню дублюючого об'єкта в масив, а не фільтрувати його пізніше, так, це теж буде добре.
Тревіс

2
Мене продовжує дивувати, як люди називають свої змінні. Іноді я думаю, що вони дійсно хочуть зробити це зайвим ускладненням. Далі побачити буде aaaaa.aaaa.push(...):)
dazito

Відповіді:


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]);

15
Ніколи не слід користуватися довжиною в циклі for, оскільки це сповільнить все, обчислюючи його на кожній ітерації. Призначте його змінній за межами циклу та передайте змінну замість Things.thing.length.
0v3rth3d4wn

12
@aefxx Я не дуже розумію цю функцію, як ви впораєтесь із ситуацією, що "місце" однакове, але ім'я інше, чи варто вважати дублом чи ні?
Куан

2
Хоча це працює, він не піклується про відсортований масив, оскільки отримання ключів ніколи не гарантується. Отже, ви закінчите їх сортування ще раз. Тепер, припустимо, масив не був відсортований, але все-таки його порядок важливий, немає ніякого способу переконатися, що замовлення залишається неушкодженим
Deepak GM

1
@DeepakGM Ви абсолютно праві. Відповідь не обов'язково збереже заданий порядок. Якщо це вимога, слід шукати інше рішення.
aefxx

Як я міг модифікувати вище, щоб видалити об'єкти з масиву, що містить X, а також усунутий?
Райан Холтон

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


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 (функція (obj, індекс, самоврядування) {return index === self.findIndex (function (t) {return t ['obj-property'] === obj ['obj-property'] });}); Переконайтеся, що ви використовуєте правильний синтаксис JS.
Мохаммед Салем Ламірі

Це правильний синтаксис JS. Ваш не використовує 1) функції жирної стрілки 2) неявне повернення або 3) позначення крапок. Ваш синтаксис ES5. Інші - це здебільшого ES6 (ECMA2015). Усі дійсні в 2017 році. Дивіться коментар jaredwilli.
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'); 

ОНОВЛЕННЯ : Тепер Лодаш також представив .uniqByа.


3
@Praveen Pds: Я щось сказав про підкреслення в прикладі коду? Я сказав, що "lodash" має цю функцію, і підкреслення має подібні. Перш ніж проголосувати, будь ласка, уважно прочитайте відповіді.
ambodi

// Перераховує унікальні об'єкти, використовуючи _underscore.js holdingObject = _.uniq (holdingObject, функція (елемент, ключ, назва) {return item.name;});
praveenpds

26
Примітка: тепер вам потрібно використовувати uniqByзамість uniq, наприклад, _.uniqBy(data, 'name')... документацію: lodash.com/docs#uniqBy
drmrbrewer

82

У мене була така ж вимога - видалити повторювані об'єкти з масиву, спираючись на дублікати в одному полі. Я знайшов тут код: 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.

це найкращий спосіб, тому що важливо знати, що це таке, що ви не хочете дублювати. Тепер це можна зробити за допомогою редуктора для стандартів e6?
Крістіан Матвій

69

Найкоротший один вкладиш для 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))створює об'єкт Set з використанням строфікованих елементів myData.
  2. Встановлений об’єкт забезпечить унікальність кожного елемента.
  3. Потім я створюю масив на основі елементів створеного набору за допомогою Array.from.
  4. Нарешті, я використовую JSON.parse для перетворення строкованого елемента назад в об'єкт.

18
проблема: {a: 1, b: 2} звичайно не буде {b: 2, a: 1}
PirateApp

2
майте на увазі, що виникнуть проблеми з властивостями дати
MarkosyanArtur

Цей рядок створює випадкові нульові значення з рядковим об'єктом, які не існують у вихідному масиві об'єктів. Чи можете ви допомогти?
B1K

46

Використовуючи 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 елементами; обраний ключ в якості першого елемента і весь вихідного елемента в якості другого елементу, це називається запис (напр. , запис масиву , Записи на карті ). А ось офіційний документ з прикладом показуючи , як додавати елементи масиву в карті конструктора.

Приклад, коли ключ розміщено :

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

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

нова карта (вхідний масив щойно відображений)

По-третє, ми використовуємо значення карт для отримання оригінальних елементів, але цього разу без дублікатів.

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

І останнє - додати ці значення до нового свіжого масиву, щоб він виглядав як початкова структура і повертав це:

повернути [... нову карту (mappedArr) .values ​​()]


Це не дає відповіді на початкове запитання, оскільки це пошук за запитом id. Питання потребує того, щоб весь об’єкт був унікальним у всіх сферах, таких як placeіname
L. Holanda

Ваша функція ES6 видається дуже стислою та практичною. Чи можете ви пояснити це трохи більше? Що саме відбувається? Видаляються перший чи останній дублікат? Або випадково, який дублікат видаляється? Це було б корисно, дякую.
Девід Шуман

Наскільки я можу сказати, створюється Карта зі значенням властивості як ключовим. Але це не на 100%, як і чи зберігається порядок масиву.
Девід Шуман

1
Привіт @DavidSchumann, я оновлю відповідь і поясню, як це працює. Але для короткої відповіді порядок зберігається, і перший видаляється ... Подумайте, як він вставлений у карту ... він перевіряє, чи ключ вже існує, оновить його, тому останній залишиться
V .Сембор

30

Ось ще один варіант зробити це за допомогою методів ітерації масиву, якщо вам потрібно порівняння лише по одному полі об’єкта:

    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

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 . Деякі існуючі реалізації можуть поєднувати два етапи, наприклад, 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;
    }

Це не працюватиме для загальних об'єктів без природного порядку сортування.
Tim Down

Правда, я додав додану користувачем порівняльну версію.
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 за деяким критерієм замовлення) повернути -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
Додано пояснення щодо переповнення стека, щоб додати пояснення, чому ваше рішення має працювати, особливо, наскільки ваше краще, ніж інші відповіді. Для отримання додаткової інформації читайте як відповісти .
Самуель Liew

Це не дає відповіді на початкове запитання, оскільки це пошук за запитом id. Питання потребує, щоб весь об’єкт був унікальним у всіх сферах, таких як placeіname
L. Holanda

16

Це загальний спосіб цього: ви переходите до функції, яка перевіряє, чи два елементи масиву вважаються рівними. У цьому випадку вона порівнює значення 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
Два об'єкти не оцінюються рівними, навіть якщо вони мають однакові властивості та значення.
kennebec

Так, я знаю. Але чесно кажучи, я не зміг правильно прочитати питання: я не помітив, що це були об'єкти з однаковими властивостями, які йому потрібно було відрізати. Я відредагую свою відповідь.
Тім Даун

1
замість того, щоб знаходитись всередині arrayContains- використовуйте Array.prototype..some метод Повертає істину, якщо один із членів масиву відповідає умові
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"
    }
]

+1, приємно пояснюючи трохи більше внутрішню роботу дедупінгу. Добре було б - з точки зору я тепер розумію зменшення: 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
L. Holanda

Це уточнення вищезазначеного узагальнення проблеми. Оригінальне запитання було розміщено 9 років тому, тому, мабуть, оригінальний плакат не хвилює placeі nameсьогодні. Кожен, хто читає цю тему, шукає оптимальний спосіб вивести список об’єктів, і це компактний спосіб зробити це.
Кліф Холл

11

Рішення TypeScript

Це видалить повторювані об'єкти, а також збереже типи об'єктів.

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

2
це чудово і коротко!
mojjj

І надто повільний теж ...
Л.

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
Ні лодаш uniq, ні uniqBy не зробили трюк, але ваше рішення зробило. Дякую! Будь ласка, вкажіть джерело свого коду, якщо це пряма копія. lodash.com/docs/4.17.10#uniqWith
Ману CJ

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;
    },[]);

для мене це вийшло чудово - я поєднав це з 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 порожньому об'єкту.

Далі ми проціджуємо масив об’єктів. Filter створює новий масив з усіма елементами, які проходять тест, реалізований наданою функцією.

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

Вище ми використовуємо функцію короткого замикання &&. Якщо ліва частина && оцінює значення true, то вона повертає значення праворуч від &&. Якщо ліва сторона помилкова, вона повертає те, що знаходиться в лівій частині &&.

Для кожного об'єкта (obj) ми перевіряємо uniq на властивість з назвою значення obj.id (У цьому випадку на першій ітерації він би перевіряв властивість '1'.) Ми хочемо навпаки тому, що він повертає або помилково) саме тому ми використовуємо! в! uniq [obj.id]. Якщо uniq вже має властивість id, він повертає true, який оцінює false (!), Кажучи функції фільтра NOT, щоб додати цей obj. Однак якщо він не знайде властивість obj.id, він поверне помилкове значення, яке потім оцінюється на true (!) І поверне все праворуч від &&, або (uniq [obj.id] = true). Це truthy значення, що говорить методу фільтра, щоб додати цей obj до повернутого масиву, а також додає властивість {1: true} до uniq. Це гарантує, що будь-який інший примірник об’єкта з тим самим ідентифікатором не буде доданий знову.


Можливо, поясніть свій код і як він відповідає на запитання?
mix3d

Спасибі, mix3d. Я додав уточнення.
MarkN

Дякуємо, що пояснили це! Це рішення працює для мене, і схоже на пару інших, розміщених тут, хоча я не розумів, що відбувається :)
Тім Моллой

3
let data = [
  {
    'name': 'Amir',
    'surname': 'Rahnama'
  }, 
  {
    'name': 'Amir',
    'surname': 'Stevens'
  }
];
let non_duplicated_data = _.uniqBy(data, 'name');

9
Будь ласка, додайте пояснення навколо свого коду, щоб майбутні відвідувачі могли зрозуміти, що це ви робите. Дякую.
Помилки

Ваша відповідь залежить від зовнішньої бібліотеки коду ...
Тейлор А. Ліч

3

Я вважаю , що поєднання reduceз JSON.stringifyдо ідеально порівняння об'єктів і вибірково додавати тих , хто ще не в акумуляторі елегантний спосіб.

Майте на увазі, що це JSON.stringifyможе стати проблемою продуктивності у крайніх випадках, коли у масиву є багато Об'єктів, і вони складні, Але більшу частину часу це найкоротший шлях до ІМХО.

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]
, [])

той, що працює для мене! Дякую тобі!
javascript110899

2

Продовжуючи вивчення ES6 способів видалення дублікатів з масиву об’єктів: thisArgаргумент встановлення Array.prototype.filterдля 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)

Таким чином, вам потрібно лише порівняти поточний елемент з попереднім елементом у масиві. Сортування одного разу перед фільтруванням ( 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 чи ні. якщо він існує, поверніть помилкове, інакше поверніть істинне. Відповідно до цього результату, натисніть об’єкт у новий масив 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 (який повертає без версії масиву, в якому зберігається лише перше виникнення кожного елемента), за винятком того, що він приймає ітерацію, яка викликається для кожного елемента в масиві, щоб генерувати критерій, за яким обчислюється унікальність.

Так, наприклад, якщо ви хочете повернути масив, що має унікальний атрибут 'place'

_.uniqBy (речі. щось, 'місце')

Аналогічно, якщо ви хочете унікальний атрибут як "ім'я"

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

Сподіваюсь, це допомагає.

Ура!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.