Видалення повторюваних об'єктів за допомогою підкреслення для Javascript


124

У мене є такий тип масиву:

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];

Я хотів би відфільтрувати його, щоб він мав:

var bar = [ { "a" : "1" }, { "b" : "2" }];

Я спробував використовувати _.uniq, але, мабуть, через те { "a" : "1" }, що не дорівнює собі, він не працює. Чи є спосіб забезпечити підкреслення uniq функцією переокремленої рівності?


Будь ласка, опублікуйте також свій код
Четтер Хаммін

Чи { "a" : "2" }існують такі речі ? Якщо так, то атрибут чи значення робить його унікальним?
Метт

Так, у мене є атрибут як ключовий, я реалізував індекс, який хтось показав мені на іншу тему, але потім я хотів очистити свій код за допомогою деяких загальних бібліотек
плюс

1
Прохання змінити прийняту відповідь.
Vadorequest

Відповіді:


232

.uniq / .unique приймає зворотний дзвінок

var list = [{a:1,b:5},{a:1,c:5},{a:2},{a:3},{a:4},{a:3},{a:2}];

var uniqueList = _.uniq(list, function(item, key, a) { 
    return item.a;
});

// uniqueList = [Object {a=1, b=5}, Object {a=2}, Object {a=3}, Object {a=4}]

Примітки:

  1. Значення повернення виклику, яке використовується для порівняння
  2. Перший об'єкт порівняння з унікальним повернутим значенням, що використовується як унікальний
  3. underscorejs.org демонструє відсутність використання зворотного дзвінка
  4. lodash.com показує використання

Ще один приклад: використання зворотного дзвінка для вилучення автомобільних марок, кольорів зі списку


falseне потрібно для _.uniq(). Також у lodash ви могли написати це так _.uniq(a, 'a');, оскільки він буде вищипувати властивість aна об'єктах.
Битва Ларрі

"Скорочення зворотного виклику" _.pluck "працює лише у тому випадку, якщо ви передаєте значення для isSorted (напр. _.uniq(a, false, 'a')) Я накрутив github / bestiejs / lodash, і вони сказали, що проблема виправлена ​​на межі. Тож якщо ви не використовуєте функцію, переконайтеся, що у вас є останні. Це може бути проблемою для підкреслення.
Шанімаль

2
Ітератор не здається хорошим ім'ям, це хеш-функція, яка буде визначати ідентичність кожного об'єкта
Juan Mendes

Відредаговано, щоб використовувати зворотний дзвінок, щоб він відповідав документам lodash :)
Shanimal

1
Ви, наприклад, на jsbin можуть мати оновлення. (1) Створює: _ (машини) .uniq ('make'). Map ('make'). ValueOf () AND (2) Colors: _ (cars) .uniq ('color'). Map ('color' ) .valueOf (). Можна визріти колір і зробити закриття. (Все, якщо ви використовуєте оновлення de lodash)
Vitor Tyburski

38

Якщо ви хочете видалити дублікати на основі ідентифікатора, ви можете зробити щось подібне:

var res = [
  {id: 1, content: 'heeey'},
  {id: 2, content: 'woah'}, 
  {id: 1, content:'foo'},
  {id: 1, content: 'heeey'},
];
var uniques = _.map(_.groupBy(res,function(doc){
  return doc.id;
}),function(grouped){
  return grouped[0];
});

//uniques
//[{id: 1, content: 'heeey'},{id: 2, content: 'woah'}]

Прийнята відповідь не працює, коли унікальним ідентифікатором є Date. Однак це робить.
gunwin

17

Реалізація відповіді Шиплу.

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];

var x = _.uniq( _.collect( foo, function( x ){
    return JSON.stringify( x );
}));

console.log( x ); // returns [ { "a" : "1" }, { "b" : "2" } ]

btw, як ви отримали 4 оновлення? Щоб отримати властивості результату, вам доведеться повернути кожне значення масиву назад в об’єкт. Щось на зразок JSON.parse(x[0]).aтому, що x не є масивом об'єктів, це масив рядків. Крім того, якщо ви додаєте значення b до унікалів та інвертуєте порядок a / b, вони більше не вважаються унікальними за вашою функцією. (наприклад, `" {\ "a \": \ "1 \", \ "b \": 2} "! =" {\ "b \": 2, \ "a \": \ "1 \"} ") Можливо, їм щось не вистачає, але чи не повинен результат принаймні бути корисним? Ось jsbin для ілюстрації jsbin.com/utoruz/2/edit
Shanimal

1
Ви маєте рацію, умова мати однакові ключі, але в іншому порядку порушує виконання. Але я не впевнений, чому ви лише перевіряєте ключ aдля кожного об'єкта, коли можуть бути дублікати об’єктів, що не містять ключ a. Однак було б сенс, якби це aбув унікальний ідентифікатор.
Битва Ларрі

Коли я відповідав на питання, мені здавалося, що у центрі уваги питання було переосмислення (a ==(=) b when a = b = {a:1}). Точкою моєї відповіді був ітератор. Я намагався відповісти, не переймаючись мотивом, який може бути будь-що, правда? (наприклад, можливо, вони хотіли витягти список марок, кольорів зі списку машин у шоу. jsbin.com/evodub/2/edit ) Будьте здорові!
Shanimal

Також я думаю, що це допомагає нам дати стислі відповіді, коли хтось, хто задає питання, надає мотив. Це гонка, тому я вважаю за краще бути першим і уточнити, якщо потрібно. З Днем Святого Патріка.
Shanimal

Ну, я просто дав ще один висновок, оскільки це відповів на моє запитання щодо порівняння вкладених масивів. iterator
Шукав

15

Коли у мене є ідентифікатор атрибута, це мій вподобаний спосіб підкреслення:

var x = [{i:2}, {i:2, x:42}, {i:4}, {i:3}];
_.chain(x).indexBy("i").values().value();
// > [{i:2, x:42}, {i:4}, {i:3}]


10

Ось просте рішення, яке використовує глибоке порівняння об’єктів, щоб перевірити наявність дублікатів (не вдаючись до перетворення в JSON, що неефективно і хакі)

var newArr = _.filter(oldArr, function (element, index) {
    // tests if the element has a duplicate in the rest of the array
    for(index += 1; index < oldArr.length; index += 1) {
        if (_.isEqual(element, oldArr[index])) {
            return false;
        }
    }
    return true;
});

Він відфільтровує всі елементи, якщо вони мають дублікат пізніше в масиві - таким, що зберігається останній дублікат.

Тестування дублікату використання, _.isEqualяке виконує оптимізоване глибоке порівняння між двома об'єктами, див. Підкреслення isEqual документації для отримання додаткової інформації.

редагувати: оновлено для використання, _.filterщо є більш чистим підходом


Чи не залежить від того, щоб мати заздалегідь задану унікальну властивість? Мені це подобається.
Дон МакКерді

1
Хороше рішення для невеликого масиву об’єктів, але цикл у циклі є дорогим порівняно з наданням uniq id.
пеннер


7

Спробуйте функцію ітератора

Наприклад, ви можете повернути перший елемент

x = [['a',1],['b',2],['a',1]]

_.uniq(x,false,function(i){  

   return i[0]   //'a','b'

})

=> [['a', 1], ['b', 2]]


аргумент секунд насправді необов’язковий, ви також можете зробити_.uniq(x,function(i){ return i[0]; });
jakecraige

3

ось моє рішення (coffeescript):

_.mixin
  deepUniq: (coll) ->
    result = []
    remove_first_el_duplicates = (coll2) ->

      rest = _.rest(coll2)
      first = _.first(coll2)
      result.push first
      equalsFirst = (el) -> _.isEqual(el,first)

      newColl = _.reject rest, equalsFirst

      unless _.isEmpty newColl
        remove_first_el_duplicates newColl

    remove_first_el_duplicates(coll)
    result

приклад:

_.deepUniq([ {a:1,b:12}, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ],[ 2, 1, 2, 1 ], {a:1,b:12} ]) 
//=> [ { a: 1, b: 12 }, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ] ]

3

з підкресленням я повинен був використовувати String () в iteratee функції

function isUniq(item) {
    return String(item.user);
}
var myUniqArray = _.uniq(myArray, isUniq);

0

Я хотів вирішити це просте рішення простим способом написання, трохи поболівши обчислювальні витрати ... але хіба це не банальне рішення з мінімальним визначенням змінної, чи не так?

function uniq(ArrayObjects){
  var out = []
  ArrayObjects.map(obj => {
    if(_.every(out, outobj => !_.isEqual(obj, outobj))) out.push(obj)
  })
  return out
}

0
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) { 
        return JSON.stringify(f); 
    }), function (gr) { 
        return gr[0]; 
    }
);

Давайте зламаємо це. Спочатку давайте групувати елементи масиву за їх строковим значенням

var grouped = _.groupBy(foo, function (f) { 
    return JSON.stringify(f); 
});

grouped виглядає наче:

{
    '{ "a" : "1" }' = [ { "a" : "1" } { "a" : "1" } ],
    '{ "b" : "2" }' = [ { "b" : "2" } ]
}

Потім давайте захопимо перший елемент з кожної групи

var bar = _.map(grouped, function(gr)
    return gr[0]; 
});

bar виглядає наче: [ { "a" : "1" }, { "b" : "2" } ]

Зберіть це все разом:

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) { 
        return JSON.stringify(f); 
    }), function (gr) { 
        return gr[0]; 
    }
);

3
Ласкаво просимо в stackoverflow. На додаток до наданого вами коду, спробуйте дати пояснення щодо того, як і як це вирішує проблему.
jtate

гарний дзвінок. Дякую. Оновлено інформацію про те, як це працює.
Келлі Біглі

-5

Ви можете це зробити за допомогою скорочення:

_.uniq(foo, 'a')


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