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

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

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

30 ответов


давайте посмотрим ... примитивным было бы:

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

хорошо, я думаю, что это должно сделать трюк. Зацени, Трэвис.

редактировать
Отредактировал код, чтобы правильно ссылаться на place (бывший id) собственность .


Как насчет некоторых es6 магия?

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

ссылка URL

для интерфейсов это может быть немного рано для реализации, так как многие используемые браузеры по-прежнему не поддерживают функции es6


если вы можете использовать библиотеки Javascript, такие как подчеркивание или lodash, я рекомендую взглянуть на


у меня было точно такое же требование, чтобы удалить дубликаты объектов в массиве на основе дубликатов в одном поле. Я нашел код здесь: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"}]

Если вы можете подождать, чтобы устранить дубликаты до тех пор, пока все дополнения, типичный подход-сначала отсортировать массив, а затем устранить дубликаты. Сортировка позволяет избежать 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;
    }

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

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

один лайнер с помощью 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. Set object гарантирует, что каждый элемент уникален.
  3. затем я создаю массив на основе элементов созданного набора с помощью Array.от.
  4. наконец, я использую JSON.parse для преобразования строкового элемента обратно в объект.

обновлено

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

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

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

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

вы также можете использовать 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"
    }
]

учитывая 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 }]

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

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'}];

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

let arr = [
  {id:1,name:"sravan"},
  {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}),{})))

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

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

вот решение для 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' }]

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

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²)).


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

рассмотрите возможность создания такого массива

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 (вещи.вещь, 'место')

аналогично, если вы хотите уникальный атрибут 'имя'

_.uniqBy (вещи.вещь, "имя")

надеюсь, что это помогает.

Ура!


Если вы не хотите указывать список свойств:

function removeDuplicates(myArr) {
  var props = Object.keys(myArr[0])
  return myArr.filter((item, index, self) =>
    index === self.findIndex((t) => (
      props.every(prop => {
        return t[prop] === item[prop]
      })
    ))
  )
}

OBS! Не совместим с IE11.


другой способ - использовать функцию уменьшения и иметь новый массив для аккумулятора. Если уже есть thing с тем же именем в массиве аккумуляторов, затем не добавляйте его туда.

let list = things.thing;
list = list.reduce((accumulator, thing) => {
    if (!accumulator.filter((duplicate) => thing.name === duplicate.name)[0]) {
        accumulator.push(thing);
    }
    return accumulator;
}, []);
thing.things = list;

я добавляю этот ответ, потому что я не смог найти хорошее, читаемое решение es6 (я использую babel для обработки функций со стрелками), совместимое с Internet Explorer 11. Проблема в IE11 нет Map.values() или Set.values() без polyfill. По той же причине я использовал filter()[0] в получить первый элемент вместо find().


 var testArray= ['a','b','c','d','e','b','c','d'];

 function removeDuplicatesFromArray(arr){

 var obj={};
 var uniqueArr=[];
 for(var i=0;i<arr.length;i++){ 
    if(!obj.hasOwnProperty(arr[i])){
        obj[arr[i]] = arr[i];
        uniqueArr.push(arr[i]);
    }
 }

return uniqueArr;

}
var newArr = removeDuplicatesFromArray(testArray);
console.log(newArr);

Output:- [ 'a', 'b', 'c', 'd', 'e' ]

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, то она возвращает значение справа от &&. Если левая сторона false, он возвращает то, что находится на левой стороне &&.

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


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


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)

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

вот еще один метод, чтобы найти количество дубликатов и легко удалить его из объекта данных. "dupsCount" - количество повторяющихся файлов. сначала отсортируйте данные, затем удалите. это даст вам быстрое удаление дублирования.

  dataArray.sort(function (a, b) {
            var textA = a.name.toUpperCase();
            var textB = b.name.toUpperCase();
            return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
        });
        for (var i = 0; i < dataArray.length - 1; ) {
            if (dataArray[i].name == dataArray[i + 1].name) {
                dupsCount++;
                dataArray.splice(i, 1);
            } else {
                i++;
            }
        }

вот решение, использующее новую функцию фильтра JavaScript, которая довольно проста . Допустим, у вас есть такой массив.

var duplicatesArray = ['AKASH','AKASH','NAVIN','HARISH','NAVIN','HARISH','AKASH','MANJULIKA','AKASH','TAPASWENI','MANJULIKA','HARISH','TAPASWENI','AKASH','MANISH','HARISH','TAPASWENI','MANJULIKA','MANISH'];

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

var uniqueArray = duplicatesArray.filter(function(elem, pos) {return duplicatesArray.indexOf(elem) == pos;});

в этом сценарии Ваш уникальный массив будет проходить через все значения в дублирующемся массиве. Переменная elem представляет значение элемент в массиве (Майк, Джеймс, Джеймс, Алекс), позиция-это 0-индексированная позиция в массиве (0,1,2,3...), и duplicatesArray.значение indexOf (elem) - это только индекс первого вхождения этого элемента в исходный массив. Итак, поскольку элемент "Джеймс" дублируется, когда мы проходим через все элементы в duplicatesArray и нажимаем их на uniqueArray, в первый раз, когда мы ударяем Джеймса, наше значение "pos" равно 1, и наш indexOf(elem) также равен 1, поэтому Джеймс получает толчок к в uniqueArray. Во второй раз, когда мы ударили Джеймса, наше значение " pos " равно 2, а наш indexOf(elem) по-прежнему равен 1 (потому что он находит только первый экземпляр элемента массива), поэтому дубликат не нажимается. Поэтому наш uniqueArray содержит только уникальные значения.

вот демонстрация вышеуказанной функции.Нажмите здесь для приведенного выше примера функции


Если вам нужен уникальный массив, основанный на нескольких свойствах объекта, вы можете сделать это с помощью map и объединения свойств объекта.

    var hash = array.map(function(element){
        var string = ''
        for (var key in element){
            string += element[key]
        }
        return string
    })
    array = array.filter(function(element, index){
        var string = ''
        for (var key in element){
            string += element[key]
        }
        return hash.indexOf(string) == index
    })

Generic для любого массива объектов:

/**
* Remove duplicated values without losing information
*/
const removeValues = (items, key) => {
  let tmp = {};

  items.forEach(item => {
    tmp[item[key]] = (!tmp[item[key]]) ? item : Object.assign(tmp[item[key]], item);
  });
  items = [];
  Object.keys(tmp).forEach(key => items.push(tmp[key]));

  return items;
}

надеюсь, что это может помочь кому угодно.


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

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

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"
}]

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