ลบรายการซ้ำออกจากอาร์เรย์ของวัตถุใน 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"});

ฉันสงสัยว่าวิธีใดดีที่สุดในการลบวัตถุที่ซ้ำกันออกจากอาร์เรย์ ตัวอย่างเช่นทุกสิ่งทุกอย่างจะกลายเป็น ...

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

คุณหมายถึงคุณจะหยุด hashtable / object ด้วยพารามิเตอร์เดียวกันทั้งหมดที่ถูกเพิ่มเข้าไปในอาร์เรย์ได้อย่างไร?
แมทธิวล็อค

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
คุณไม่ควรใช้ความยาวในการวนรอบเพราะจะทำให้ทุกอย่างช้าลงการคำนวณในการวนซ้ำทุกครั้ง กำหนดให้กับตัวแปรภายนอกลูปและส่งผ่านตัวแปรแทนสิ่งของทุกอย่างความยาว
0v3rth3d4wn

12
@ aefxx ฉันไม่ค่อยเข้าใจฟังก์ชั่นนี้คุณจะรับมือกับสถานการณ์ที่ว่า "สถานที่" เหมือนกันได้อย่างไร แต่ชื่อนั้นแตกต่างกันซึ่งควรพิจารณาด้วยซ้ำหรือไม่?
Kuan

2
แม้ว่าจะได้ผล แต่ก็ไม่ได้ดูแลอาเรย์ที่เรียงลำดับเนื่องจากการดึงข้อมูลคีย์นั้นไม่เคยรับประกันการสั่งซื้อ ดังนั้นคุณจะเรียงลำดับอีกครั้ง ทีนี้สมมติว่าอาเรย์ไม่ได้เรียงลำดับ แต่ก็เป็นสิ่งสำคัญลำดับไม่มีวิธีที่คุณสามารถตรวจสอบให้แน่ใจว่าใบสั่งยังคงเหมือนเดิม
Deepak GM

1
@DeepakGM คุณพูดถูก คำตอบจะไม่รักษาคำสั่งซื้อ (จำเป็น) หากนั่นคือข้อกำหนดหนึ่งควรมองหาวิธีอื่น
aefxx

ฉันจะปรับเปลี่ยนข้างต้นเพื่อลบวัตถุออกจากอาร์เรย์ที่มี X รวมถึงการทำซ้ำได้อย่างไร
Ryan Holton

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)
Josh Cole

ทำงานได้อย่างสมบูรณ์แบบ! var uniqueArrayOfObjects = arrayOfObjects.filter (ฟังก์ชั่น (obj, index, self) {return index === self.findIndex (ฟังก์ชั่น (t) {return t ['obj-property'] === obj ['obj-property'] });}); ตรวจสอบให้แน่ใจว่าคุณใช้ไวยากรณ์ JS ที่เหมาะสม
Mohamed Salem Lamiri

สิ่งเหล่านี้เป็นไวยากรณ์ JS ที่เหมาะสม ของคุณไม่ได้ใช้ 1) ฟังก์ชั่นลูกศรไขมัน 2) การส่งคืนโดยนัยหรือ 3) เครื่องหมายจุด คุณเป็นไวยากรณ์ของ ES5 ส่วนใหญ่เป็น ES6 (ECMA2015) ทั้งหมดถูกต้องในปี 2560 ดูความคิดเห็นของ 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
Eydrian

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

ปรับปรุง : Lodash ตอนนี้ได้เปิดตัว.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: ลบรายการที่ซ้ำออกจาก Array of Objects

ดังนั้นในตัวอย่างของฉันฉันกำลังลบวัตถุใด ๆ ออกจากอาร์เรย์ที่มีค่าสตริงที่อนุญาตซ้ำกัน

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'); } }
Abdul Sadik Yalcin

ทำไมไม่ลบความซับซ้อน 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 หรือไม่?
Christian Matthew

69

สั้นที่สุดหนึ่ง liners สำหรับ 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
Magic นี่เป็นคำตอบที่แท้จริง
Luis Contreras

48

ซับหนึ่งชุดใช้

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))สร้างวัตถุชุดโดยใช้องค์ประกอบ myData stringified
  2. ชุดวัตถุจะให้แน่ใจว่าองค์ประกอบทุกอย่างไม่ซ้ำกัน
  3. จากนั้นฉันสร้างอาร์เรย์ตามองค์ประกอบของชุดที่สร้างขึ้นโดยใช้ Array.from
  4. สุดท้ายฉันใช้ JSON.parse เพื่อแปลงองค์ประกอบที่เป็นสตริงกลับไปเป็นวัตถุ

18
ปัญหาคือ {a: 1, b: 2} จะไม่เท่ากับ {b: 2, a: 1}
PirateApp

2
โปรดทราบว่าจะมีปัญหากับคุณสมบัติของวันที่
MarkosyanArtur

บรรทัดนี้สร้างค่า null แบบสุ่มด้วยวัตถุแถวที่ไม่มีอยู่ในอาร์เรย์ของวัตถุดั้งเดิม คุณช่วยได้ไหม
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" }], ...]

ประการที่สองเราส่งอาร์เรย์ที่ปรับเปลี่ยนนี้ไปยังตัวสร้างแผนที่และนี่คือสิ่งมหัศจรรย์ที่เกิดขึ้น แผนที่จะกำจัดค่าของคีย์ที่ซ้ำกันโดยคงไว้ซึ่งค่าที่แทรกไว้ล่าสุดของคีย์เดียวกันเท่านั้น หมายเหตุ : แผนที่เก็บคำสั่งของการแทรก ( ตรวจสอบความแตกต่างระหว่างแผนที่และวัตถุ )

แผนที่ใหม่ (รายการอาร์เรย์ที่แมปไว้ด้านบน)

อันดับที่สามเราใช้ค่าแผนที่เพื่อดึงรายการต้นฉบับ แต่คราวนี้ไม่มีการทำซ้ำ

แผนที่ใหม่ (mappedArr) .values ​​()

และอันสุดท้ายคือการเพิ่มค่าเหล่านั้นลงในอาร์เรย์ใหม่ที่สดใหม่เพื่อให้มันสามารถดูเป็นโครงสร้างเริ่มต้นและกลับมาที่:

ส่งคืน [... แผนที่ใหม่ (mappedArr) .values ​​()]


idนี้ไม่ตอบคำถามเดิมเช่นนี้เป็นการค้นหาสำหรับ คำถามต้องการวัตถุทั้งหมดที่จะไม่ซ้ำกันในทุกสาขาเช่นplaceและname
L. Holanda

ฟังก์ชั่น ES6 ของคุณดูเหมือนจะกระชับและใช้งานได้จริง คุณช่วยอธิบายอีกหน่อยได้ไหม? เกิดอะไรขึ้นกันแน่? รายการแรกหรือรายการที่ซ้ำกันถูกลบออกหรือไม่ หรือมันสุ่มที่จะได้รับการลบที่ซ้ำกัน? นั่นจะเป็นประโยชน์ขอบคุณ
David Schumann

เท่าที่ฉันสามารถบอกได้แผนที่ที่มีค่าคุณสมบัติเป็นคีย์จะถูกสร้างขึ้น แต่มันจะไม่ 100% อย่างไรหรือถ้าลำดับของอาร์เรย์ถูกเก็บรักษาไว้
David Schumann

1
สวัสดี @DavidSchumann ฉันจะอัปเดตคำตอบและจะอธิบายวิธีการทำงาน แต่สำหรับคำตอบสั้น ๆ คำสั่งซื้อจะถูกเก็บรักษาไว้และคำสั่งแรกจะถูกลบออก ... แค่คิดว่ามันถูกแทรกลงในแผนที่ ... มันตรวจสอบว่ากุญแจมีอยู่แล้วมันจะอัปเดตหรือไม่ดังนั้นสุดท้ายจะยังคงอยู่
V Sambor

30

นี่เป็นอีกตัวเลือกหนึ่งในการใช้ Array iterating method ถ้าคุณต้องการเปรียบเทียบโดยหนึ่งฟิลด์ของวัตถุ:

    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
ดีและสะอาดถ้าคุณต้องการลบวัตถุที่มีค่าซ้ำกันไม่สะอาดสำหรับวัตถุที่ทำซ้ำอย่างสมบูรณ์
David Barker

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
เป็นวิธีปฏิบัติที่ดีสำหรับ Stack Overflow เพื่อเพิ่มคำอธิบายว่าทำไมโซลูชันของคุณจึงทำงานได้ดีโดยเฉพาะอย่างยิ่งวิธีที่คุณตอบได้ดีกว่าคำตอบอื่น ๆ สำหรับข้อมูลเพิ่มเติมอ่านวิธีการตอบ
ซามูเอล 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

ใช่ฉันรู้. แต่ประเด็นที่เป็นธรรมฉันไม่สามารถอ่านคำถามได้อย่างถูกต้อง: ฉันไม่ได้เห็นว่ามันเป็นวัตถุที่มีคุณสมบัติเหมือนกันที่เขาต้องการกำจัดออก ฉันจะแก้ไขคำตอบของฉัน
Tim Down

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, ผู้ที่ดีอธิบายเพิ่มเติมอีกเล็กน้อยการทำงานภายในของ dedupThings จะดี - ในด้านสว่างฉันเข้าใจตอนนี้ลด: 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

และช้าที่สุดด้วย ...
L. Holanda

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 ของ lodash หรือ uniqBy ไม่ได้หลอกลวง แต่โซลูชันของคุณทำได้ ขอบคุณ! โปรดให้รหัสแหล่งที่มาของคุณอย่างไรก็ตามหากเป็นสำเนาโดยตรง lodash.com/docs/4.17.10#uniqWith
Manu 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)) {แทนที่จะมองหา @ a single property
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]อาร์เรย์คู่ (length = 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 เป็นวัตถุว่าง

ต่อไปเราจะกรองอาร์เรย์ของวัตถุ ตัวกรองสร้างอาร์เรย์ใหม่พร้อมองค์ประกอบทั้งหมดที่ผ่านการทดสอบที่ใช้งานโดยฟังก์ชันที่มีให้

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

ข้างต้นเราใช้ฟังก์ชันการลัดวงจรของ && หากด้านซ้ายของ && ประเมินเป็นจริงก็จะส่งคืนค่าทางด้านขวาของ && หากด้านซ้ายเป็นเท็จจะส่งคืนสิ่งที่อยู่ทางด้านซ้ายของ &&

สำหรับแต่ละวัตถุ (obj) เราตรวจสอบ uniq สำหรับคุณสมบัติที่มีชื่อว่าค่าของ obj.id (ในกรณีนี้ในการทำซ้ำครั้งแรกมันจะตรวจสอบคุณสมบัติ '1') เราต้องการตรงกันข้ามกับสิ่งที่มันคืน (จริง หรือเท็จ) ซึ่งเป็นเหตุผลที่เราใช้! ใน! uniq [obj.id] ถ้า uniq มีคุณสมบัติ id อยู่แล้วมันจะคืนค่าจริงซึ่งประเมินว่าเป็นเท็จ (!) แจ้งให้ฟังก์ชั่นตัวกรองไม่ต้องเพิ่ม obj นั้น อย่างไรก็ตามหากไม่พบคุณสมบัติ obj.id จะส่งคืนค่าเท็จซึ่งจะประเมินเป็นจริง (!) และส่งกลับทุกอย่างทางด้านขวาของ && หรือ (uniq [obj.id] = true) นี่เป็นค่าจริงบอกวิธีการกรองเพื่อเพิ่ม obj นั้นไปยังอาร์เรย์ที่ส่งคืนและยังเพิ่มคุณสมบัติ {1: true} ให้กับ uniq สิ่งนี้ทำให้มั่นใจได้ว่าอินสแตนซ์ obj อื่น ๆ ที่มี id เดียวกันนั้นจะไม่ถูกเพิ่มอีก


อาจอธิบายรหัสของคุณและจะตอบคำถามได้อย่างไร
mix3d

ขอบคุณ mix3d ฉันเพิ่มความกระจ่าง
MarkN

ขอบคุณที่อธิบายสิ่งนี้! วิธีนี้ใช้ได้ผลกับฉันและคล้ายกับคนอื่น ๆ ที่โพสต์ที่นี่แม้ว่าฉันจะไม่เข้าใจว่าเกิดอะไรขึ้น :)
Tim Molloy

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

9
โปรดเพิ่มคำอธิบายรอบโค้ดของคุณเพื่อให้ผู้เข้าชมในอนาคตสามารถเข้าใจว่าคุณกำลังทำอะไรอยู่ ขอบคุณ
Bugs

คำตอบของคุณขึ้นอยู่กับ
Taylor A. Leach

3

ฉันเชื่อว่าการรวมกันของreduceกับJSON.stringifyเพื่อเปรียบเทียบวัตถุอย่างสมบูรณ์และเลือกผู้ที่ไม่ได้อยู่ในการสะสมเป็นวิธีที่สง่างาม

โปรดทราบว่าJSON.stringifyอาจกลายเป็นปัญหาด้านประสิทธิภาพในกรณีที่มีอาร์เรย์ที่มีวัตถุจำนวนมากและมีความซับซ้อน แต่ส่วนใหญ่แล้วนี่เป็นวิธีที่สั้นที่สุดในการไป IMHO

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 magic ในหนึ่งบรรทัด ... อ่านได้ที่!

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

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

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


เพิ่มคำอธิบายบางอย่าง
แมทธิวส์ซันนี่

@Billa มันโอเคไหม?
Bibin Jaimon

1

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

ลองสร้างอาร์เรย์แบบนี้

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 library ที่นี่คุณสามารถใช้_.uniqBy

.uniqBy (อาร์เรย์, [iteratee = .identity])

วิธีนี้เปรียบเสมือน_.uniq (ซึ่งจะส่งกลับอาร์เรย์รุ่นที่ไม่ซ้ำกันซึ่งจะเกิดขึ้นครั้งแรกของแต่ละองค์ประกอบเท่านั้น) ยกเว้นว่าจะยอมรับ iteratee ซึ่งถูกเรียกใช้สำหรับแต่ละองค์ประกอบในอาร์เรย์เพื่อสร้างเกณฑ์โดยที่ มีการคำนวณเอกลักษณ์

ตัวอย่างเช่นหากคุณต้องการส่งคืนอาร์เรย์ที่มีแอตทริบิวต์ที่ไม่ซ้ำกันของ 'สถานที่'

_.uniqBy (Things.thing, 'place')

ในทำนองเดียวกันหากคุณต้องการแอตทริบิวต์ที่ไม่ซ้ำกันเป็น 'ชื่อ'

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

หวังว่านี่จะช่วยได้

ไชโย!

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