Xóa các đối tượng trùng lặp với Underscore cho Javascript


124

Tôi có loại mảng này:

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

Tôi muốn lọc nó để có:

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

Tôi đã thử sử dụng _.uniq, nhưng tôi đoán vì { "a" : "1" }không bằng chính nó, nó không hoạt động. Có cách nào để cung cấp unicore uniq với hàm overriden bằng không?


Vui lòng gửi mã của bạn là tốt
Chetter Hummin

Làm những thứ như { "a" : "2" }tồn tại? Nếu vậy nó là thuộc tính hoặc giá trị làm cho nó là duy nhất?
Matt

Có, tôi làm có một thuộc tính như chìa khóa, tôi thực hiện các chỉ số một ai đó chỉ cho tôi về vấn đề khác, nhưng sau đó tôi muốn để làm sạch mã của tôi sử dụng một số thư viện chung
plus-

1
Xin vui lòng thay đổi câu trả lời.
Vadorequest

Câu trả lời:


232

.uniq / .unique chấp nhận gọi lại

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

Ghi chú:

  1. Giá trị gọi lại được sử dụng để so sánh
  2. Đối tượng so sánh đầu tiên với giá trị trả về duy nhất được sử dụng là duy nhất
  3. underscorejs.org chứng minh không sử dụng gọi lại
  4. lodash.com cho thấy việc sử dụng

Một ví dụ khác: sử dụng phương thức gọi lại để trích xuất xe, màu sắc từ danh sách


falselà không cần thiết cho _.uniq(). Ngoài ra trong lodash bạn có thể đã viết nó như thế này _.uniq(a, 'a');, vì nó sẽ nhổ tài sản atrên các đối tượng.
Trận chiến

Tốc ký gọi lại "'_.pluck'" chỉ hoạt động nếu bạn chuyển một giá trị cho isSort (ví dụ _.uniq(a, false, 'a')) Tôi đã ping github / bestiejs / lodash và họ nói rằng vấn đề đã được khắc phục. Vì vậy, nếu bạn không sử dụng chức năng, hãy đảm bảo rằng bạn có bản mới nhất. Điều này có thể không phải là một vấn đề cho gạch dưới.
Shanimal

2
Iterator nghe có vẻ không phải là một cái tên hay, đó là một hàm giống như hàm băm sẽ xác định danh tính của từng đối tượng
Juan Mendes

Đã chỉnh sửa để sử dụng gọi lại để phù hợp hơn với các tài liệu lodash :)
Shanimal

1
Bạn ví dụ tại jsbin có thể có một bản cập nhật. (1) Làm cho: _ (ô tô) .uniq ('make'). Map ('make'). ValueOf () AND (2) Màu sắc: _ (ô tô) .uniq ('color'). Map ('color' ).giá trị của(). Bạn có thể chín màu và đóng cửa. (Tất cả điều đó nếu bạn nâng cấp de lodash đã sử dụng)
Vitor Tyburski

38

Nếu bạn đang tìm cách xóa các bản sao dựa trên id, bạn có thể làm một cái gì đó như thế này:

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

Câu trả lời được chấp nhận không hoạt động khi định danh duy nhất là a Date. Tuy nhiên, điều này không.
gunwin

17

Thực hiện câu trả lời của Shiplu.

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, làm thế nào bạn có được 4 upvote? Để có được các thuộc tính của kết quả của bạn, bạn phải hoàn nguyên từng giá trị mảng trở lại thành một đối tượng. Một cái gì đó giống như JSON.parse(x[0]).abởi vì x không phải là một mảng các đối tượng, nó là một chuỗi các chuỗi. Ngoài ra, nếu bạn thêm các giá trị b vào các đơn vị và đảo ngược thứ tự của a / b thì chúng không còn được coi là duy nhất bởi hàm của bạn. (ví dụ: `" {\ "a \": \ "1 \", \ "b \": 2} "! =" {\ "b \": 2, \ "a \": \ "1 \"} ") Có thể tôi đang thiếu một cái gì đó, nhưng ít nhất kết quả có hữu ích không? Dưới đây là một jsbin để minh họa jsbin.com/utoruz/2/edit
Shanimal 16/03/13

1
Bạn đúng điều kiện có cùng khóa nhưng theo thứ tự khác nhau sẽ phá vỡ việc thực hiện. Nhưng tôi không chắc tại sao bạn chỉ kiểm tra khóa acho từng đối tượng, khi có thể có các đối tượng trùng lặp không chứa khóa a. Tuy nhiên, nó sẽ có ý nghĩa nếu alà một id duy nhất.
Trận chiến

Khi tôi trả lời câu hỏi, dường như trọng tâm của câu hỏi là ghi đè (a ==(=) b when a = b = {a:1}). Điểm của câu trả lời của tôi là iterator. Tôi đã cố gắng trả lời mà không lo lắng về động cơ, có thể là bất cứ điều gì, phải không? (ví dụ: có thể họ muốn trích xuất một danh sách các nhãn hiệu, màu sắc từ danh sách xe ô tô trong một chương trình. jsbin.com/evodub/2/edit ) Chúc mừng!
Shanimal

Ngoài ra tôi nghĩ rằng nó giúp chúng tôi đưa ra câu trả lời súc tích khi ai đó hỏi một câu hỏi cung cấp động lực. Đây là một cuộc đua, vì vậy tôi thích là người đầu tiên và làm rõ nếu cần thiết. Chúc mừng ngày Thánh Patrick.
Tối

Vâng, tôi chỉ đưa ra một upvote khác bởi vì điều này đã trả lời câu hỏi của tôi về việc so sánh các mảng lồng nhau. Chỉ tìm cách ghi đè lêniterator
nevi_me

15

Khi tôi có id thuộc tính, đây là cách được đặt trước của tôi trong dấu gạch dưới:

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

12

Sử dụng lib dưới đây duy nhất làm việc cho tôi, tôi đang tạo danh sách duy nhất dựa trên _id sau đó trả về giá trị Chuỗi của _id:

var uniqueEntities = _.uniq(entities, function (item, key, a) {
                                    return item._id.toString();
                                });

10

Đây là một giải pháp đơn giản, sử dụng so sánh đối tượng sâu để kiểm tra các bản sao (mà không dùng đến việc chuyển đổi sang JSON, không hiệu quả và hacky)

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

Nó lọc ra tất cả các phần tử nếu chúng có một bản sao sau này trong mảng - sao cho phần tử trùng lặp cuối cùng được giữ lại.

Việc kiểm tra các mục đích sử dụng trùng lặp _.isEqualthực hiện so sánh sâu được tối ưu hóa giữa hai đối tượng, xem tài liệu isEqual gạch dưới để biết thêm thông tin.

chỉnh sửa: cập nhật để sử dụng _.filterđó là một cách tiếp cận sạch hơn


Không phụ thuộc vào việc có một tài sản duy nhất được xác định trước? Tôi thích nó.
Don McCurdy

1
Một giải pháp tốt cho một mảng nhỏ các đối tượng nhưng một vòng lặp trong vòng lặp đắt tiền so với việc cung cấp id uniq.
Penner


7

Hãy thử chức năng lặp

Ví dụ: bạn có thể trả về phần tử đầu tiên

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

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

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

})

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


đối số giây thực sự là tùy chọn, bạn cũng có thể làm_.uniq(x,function(i){ return i[0]; });
jakecraige

3

đây là giải pháp của tôi (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

thí dụ:

_.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

với dấu gạch dưới, tôi đã phải sử dụng String () trong hàm iteratee

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

0

Tôi muốn giải quyết giải pháp đơn giản này bằng cách viết đơn giản, với một chút đau đớn về chi phí tính toán ... nhưng đó không phải là một giải pháp tầm thường với định nghĩa biến tối thiểu, phải không?

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

Hãy phá vỡ điều này. Đầu tiên cho phép nhóm các mục mảng theo giá trị được xâu chuỗi của chúng

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

grouped giống như:

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

Sau đó, hãy lấy phần tử đầu tiên từ mỗi nhóm

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

bar giống như: [ { "a" : "1" }, { "b" : "2" } ]

Đặt nó tất cả cùng nhau:

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
Chào mừng đến với stackoverflow. Ngoài mã bạn đã cung cấp, hãy thử đưa ra một số giải thích về lý do và cách khắc phục sự cố này.
jtate

cuộc gọi tốt Cảm ơn. Cập nhật với một sự cố về cách thức này hoạt động.
Kelly Bigley

-5

Bạn có thể làm điều đó trong một tốc ký như:

_.uniq(foo, 'a')


giải pháp của bạn không hoạt động đối với mảng đối tượng, mà chỉ đối với mảng
Toucouleur
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.