การลบอ็อบเจ็กต์ที่ซ้ำกันด้วยขีดล่างสำหรับ Javascript


124

ฉันมีอาร์เรย์ประเภทนี้:

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

ฉันต้องการกรองให้มี:

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

ฉันลองใช้ _.uniq แต่ฉันเดาว่าเพราะ{ "a" : "1" }มันไม่เท่ากับตัวมันเองจึงไม่ได้ผล มีวิธีใดบ้างที่จะให้ uniq ขีดล่างด้วยฟังก์ชัน overriden equals?


กรุณาโพสต์รหัสของคุณด้วย
Chetter Hummin

สิ่งที่ต้องการ{ "a" : "2" }มีอยู่จริงหรือไม่? ถ้าเป็นเช่นนั้นแอตทริบิวต์หรือค่าที่ทำให้ไม่ซ้ำกัน?
Matt

ใช่ฉันมีแอตทริบิวต์เป็นคีย์ฉันติดตั้งดัชนีที่มีคนแสดงให้ฉันเห็นในหัวข้ออื่น แต่แล้วฉันต้องการล้างโค้ดของฉันโดยใช้ไลบรารีทั่วไป
บวก -

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จากวัตถุ
Larry Battle

การชวเลขเรียกกลับ "'_.pluck' จะใช้งานได้ก็ต่อเมื่อคุณส่งค่าสำหรับ isSorted (เช่น_.uniq(a, false, 'a')) ฉัน pinged github / bestiejs / lodash และพวกเขาบอกว่าปัญหาได้รับการแก้ไขแล้ว ดังนั้นหากคุณไม่ได้ใช้ฟังก์ชันตรวจสอบให้แน่ใจว่าคุณมีเวอร์ชันล่าสุด นี่อาจไม่ใช่ปัญหาสำหรับขีดล่าง
Shanimal

2
Iterator ฟังดูไม่เหมือนชื่อที่ดีมันเป็นฟังก์ชันที่เหมือนแฮชที่จะใช้ในการระบุเอกลักษณ์ของแต่ละวัตถุ
Juan Mendes

แก้ไขเพื่อใช้การโทรกลับเพื่อให้สอดคล้องกับ lodash docs มากขึ้น :)
Shanimal

1
คุณตัวอย่างที่jsbinสามารถมีการปรับปรุง (1) ทำให้: _ (คัน) .uniq ('make'). map ('make'). valueOf () AND (2) Colors: _ (cars) .uniq ('color'). map ('color' ).มูลค่าของ(). คุณสามารถทำให้สีสุกและปิดได้ (ทั้งหมดนั้นถ้าคุณอัพเกรด 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'}]

คำตอบที่ยอมรับจะใช้ไม่ได้เมื่อตัวระบุเฉพาะคือ a Date. อย่างไรก็ตามสิ่งนี้
gunwin

17

การดำเนินการตามคำตอบของ 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 คุณได้รับ 4 upvotes อย่างไร? ในการรับคุณสมบัติของผลลัพธ์ของคุณคุณจะต้องเปลี่ยนค่าอาร์เรย์แต่ละค่ากลับเป็นวัตถุ บางอย่างเช่นJSON.parse(x[0]).aเนื่องจาก x ไม่ใช่อาร์เรย์ของวัตถุ แต่เป็นอาร์เรย์ของสตริง นอกจากนี้หากคุณเพิ่มค่า b ให้กับ uniques และสลับลำดับของ a / b ฟังก์ชันของคุณจะไม่ถือว่าเป็นเอกลักษณ์อีกต่อไป (เช่น "{\" a \ ": \" 1 \ ", \" b \ ": 2}"! = "{\" b \ ": 2, \" a \ ": \" 1 \ "} ") บางทีฉันอาจจะพลาดบางอย่างไป แต่อย่างน้อยผลลัพธ์ก็ไม่ควรมีประโยชน์หรือ? นี่คือ jsbin เพื่อแสดงjsbin.com/utoruz/2/edit
Shanimal

1
คุณมีเงื่อนไขที่จะมีคีย์เดียวกัน แต่ในลำดับที่ต่างกันจะทำให้การนำไปใช้งานผิดพลาด แต่ผมไม่แน่ใจว่าทำไมคุณเพียงการตรวจสอบที่สำคัญสำหรับแต่ละวัตถุเมื่อมีอาจจะเป็นวัตถุที่ซ้ำกันไม่ได้มีกุญแจa aอย่างไรก็ตามมันจะสมเหตุสมผลถ้าaเป็นรหัสเฉพาะ
Larry Battle

(a ==(=) b when a = b = {a:1})เมื่อฉันได้ตอบคำถามที่มันดูเหมือนกับผมว่าจุดสำคัญของคำถามคือการแทนที่ ประเด็นของคำตอบของฉันคือตัวทำซ้ำ ฉันพยายามตอบโดยไม่ต้องกังวลเกี่ยวกับเหตุจูงใจซึ่งอาจเป็นอะไรก็ได้ใช่ไหม (เช่นบางทีพวกเขาอาจต้องการดึงรายชื่อยี่ห้อสีจากรายการรถในรายการjsbin.com/evodub/2/edit ) ไชโย!
Shanimal

นอกจากนี้ฉันคิดว่ามันช่วยให้เราให้คำตอบที่กระชับเมื่อมีคนถามคำถามให้แรงจูงใจ นี่คือการแข่งขันดังนั้นฉันจึงอยากเป็นที่หนึ่งและชี้แจงหากจำเป็น สุขสันต์วันเซนต์แพททริค
Shanimal

ฉันเพิ่งให้คะแนนโหวตเพิ่มอีกครั้งเพราะสิ่งนี้ตอบคำถามของฉันเกี่ยวกับการเปรียบเทียบอาร์เรย์ที่ซ้อนกัน กำลังมองหาวิธีการแทนที่iterator
nevi_me

15

เมื่อฉันมี id แอตทริบิวต์นี่คือวิธีที่ฉันได้รับไว้ล่วงหน้าในเครื่องหมายขีดล่าง:

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ซึ่งเป็นแนวทางที่สะอาดกว่า


ไม่ได้ขึ้นอยู่กับการมีคุณสมบัติเฉพาะที่กำหนดไว้ล่วงหน้า? ฉันชอบมัน.
Don McCurdy

1
ทางออกที่ดีสำหรับอาร์เรย์ของออบเจ็กต์ขนาดเล็ก แต่การวนซ้ำภายในลูปนั้นมีราคาแพงเมื่อเทียบกับการให้ uniq id
เพนเนอร์


7

ลองใช้ฟังก์ชัน iterator

ตัวอย่างเช่นคุณสามารถส่งคืนองค์ประกอบแรก

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

นี่คือทางออกของฉัน (กาแฟ):

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

โทร. ขอบคุณ อัปเดตพร้อมรายละเอียดวิธีการทำงาน
Kelly Bigley

-5

คุณสามารถทำได้โดยใช้ชวเลขดังนี้:

_.uniq(foo, 'a')


โซลูชันของคุณใช้ไม่ได้กับอาร์เรย์ของวัตถุ แต่สำหรับอาร์เรย์เท่านั้น
Toucouleur
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.