ลบรายการที่ซ้ำกันออกจากอาร์เรย์


100

ฉันมีอาร์เรย์ของวัตถุที่มีลักษณะเช่นนี้:

var array = [
    {id:123, value:"value1", name:"Name1"},
    {id:124, value:"value2", name:"Name1"},
    {id:125, value:"value3", name:"Name2"},
    {id:126, value:"value4", name:"Name2"}
    ...
];

อย่างที่คุณเห็นบางชื่อซ้ำกัน ฉันต้องการรับอาร์เรย์ใหม่ที่มีชื่อเท่านั้น แต่ถ้าบางชื่อซ้ำฉันไม่ต้องการเพิ่มอีก ฉันต้องการอาร์เรย์นี้:

var newArray = ["Name1", "Name2"];

ฉันกำลังพยายามทำสิ่งนี้กับmap:

var newArray = array.map((a) => {
    return a.name;
});

แต่ปัญหาคือสิ่งนี้ส่งกลับ:

newArray = ["Name1", "Name1", "Name2", "Name2"];

ฉันจะตั้งเงื่อนไขบางอย่างภายในได้mapอย่างไรจึงจะไม่ส่งคืนองค์ประกอบที่มีอยู่แล้ว ฉันต้องการทำสิ่งนี้ด้วยmapหรือคุณลักษณะ ECMAScript 5 หรือ ECMAScript 6 อื่น ๆ


2
จากนั้นลบรายการที่ซ้ำกันออกจากอาร์เรย์
Tushar


1
อาจทำซ้ำได้ของRemove Duplicates จาก JavaScript Array
Bergi

ทำไมบรรทัดที่มี id: 125 ไม่ลงท้ายด้วยลูกน้ำ?
Peter Mortensen

โปรดทราบว่าคำตอบทั้งหมดที่ใช้ .indexOf () จะมีประสิทธิภาพที่ดีถ้าอาร์เรย์ที่มีขนาดใหญ่เนื่องจากพวกเขากำลังสองเวลาซับซ้อน ฉันอยากจะแนะนำให้ใช้ES6 Setหรือใช้วัตถุ ES5เป็นพจนานุกรม
joeytwiddle

คำตอบ:


211

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

ข้อเสนอนี้ใช้ไวยากรณ์การแพร่กระจาย...สำหรับการรวบรวมรายการในอาร์เรย์ใหม่

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }],
      names = [...new Set(array.map(a => a.name))];

console.log(names);


9
สำหรับข้อมูลของฉัน .. ...ต้องทำอย่างไร?
Rajshekar Reddy

9
@RajshekarReddy เป็นไวยากรณ์แบบกระจาย...สำหรับการสร้างอาร์เรย์ด้วยวัตถุที่ทำซ้ำได้
Nina Scholz

5
ฉันไม่ได้โหวตให้ "Set" แต่เพื่อการใช้ตัวดำเนินการกระจายอย่างชาญฉลาด เป็นเรื่องดีเสมอที่ได้เรียนรู้สิ่งใหม่ ๆ หรือเห็นสิ่งใหม่ ๆ ที่นำไปใช้กับตัวอย่างที่ใช้ได้จริงดังนั้นโพสต์นี้จึงสมควรได้รับการโหวตเพิ่ม
briosheje

34
มันเป็นเพียงความเห็นส่วนตัวของฉัน แต่แทนที่จะร่วมกันอย่างใดอย่างหนึ่ง การใช้Array.fromสื่อถึงเจตนาของการแปลงได้ดีกว่ามาก (และยังง่ายต่อการทำความเข้าใจ / ค้นหามือใหม่) ควรใช้ไวยากรณ์การแพร่กระจายเฉพาะในกรณีที่องค์ประกอบกระจายเป็นหนึ่งในหลาย ๆ อาจจะเรียกมันว่า "การปฏิบัติที่ไม่ดี" ที่รุนแรงเกินไปขออภัยฉันแค่หวังว่าจะป้องกันไม่ให้มันกลายเป็นสำนวนทั่วไป
Bergi

3
@KRyan ES6 ใหม่เหรอ ได้รับการสรุปในปี 2558 มิถุนายน ถึงเวลาเริ่มทำความเข้าใจและใช้คุณสมบัติใหม่แล้ว
edc65

64

หากคุณกำลังมองหาโซลูชัน JavaScript ที่ไม่ใช่ ES 6 (no Set) คุณสามารถใช้วิธีการของ Arrayreduce :

var array=[
  {id:123, value:"value1", name:"Name1"},
  {id:124, value:"value2", name:"Name1"},
  {id:125, value:"value3", name:"Name2"},
  {id:126, value:"value4", name:"Name2"}
];
var names = array.reduce(function (a, b) {
  if (a.indexOf(b.name) == -1) {
    a.push(b.name)
  }
  return a;
}, []);

console.log(names);


2
คำตอบนี้ยังมีประโยชน์ที่ไม่ต้องใช้อาร์เรย์หรือชุดกลางโดยเฉพาะอย่างยิ่งคำตอบที่มีเฉพาะชื่อเท่านั้น (มันเปลี่ยนจากอาร์เรย์ของวัตถุไปยังอาร์เรย์ของวัตถุโดยตรง) +1
jpmc26

@Iwrestledabearonce พารามิเตอร์ตัวที่สองนี้ไม่ใช่วัตถุเดียวกับวัตถุที่ส่งคืนหรือไม่?
Kaiido

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

1
@Dekel - รุ่นเล็ก :)var names = array.reduce(function(a, b) { a.indexOf(b.name) === -1 && a.push(b.name) return a; }, []);
Rayon

3
@Rayon โปรดอย่าให้ความสำคัญกับขนาดของซอร์สโค้ดมากเกินไปนี่เป็นข้อผิดพลาดทั่วไปสำหรับโปรแกรมเมอร์ที่สร้างขึ้นเองจำนวนมากที่สำคัญที่สุดโปรแกรมของคุณควรมีประสิทธิภาพและอ่านได้
ใบ

16

โดยส่วนตัวแล้วฉันไม่เห็นว่าทำไมทุกคนถึงหลงใหลใน ES 6 หากเป็นรหัสของฉันฉันต้องการสนับสนุนเบราว์เซอร์ให้มากที่สุด

var array=[
{id:123, value:"value1", name:"Name1"},
{id:124, value:"value2", name:"Name1"},
{id:125, value:"value3", name:"Name2"},
{id:126, value:"value4", name:"Name2"}
];

   // Create array of unique names
var a = (function(a){
  for (var i = array.length; i--;)
    if (a.indexOf(array[i].name) < 0) a.push(array[i].name);
  return a;
})([]);

console.log(a);


2
การใช้ออบเจ็กต์เป็นพจนานุกรมแทนที่จะใช้indexOf-ing ตลอดเวลาจะมีประสิทธิภาพมากขึ้นหรือไม่? เช่น do added[name] = trueและif(added[name])แทนif(a.indexOf(name)).
Bojidar Marinov

7
"โดยส่วนตัวฉันไม่เห็นว่าทำไมทุกคนถึงรู้สึกแฟนซีกับ es6" ทำไมคุณถึงติดพัน a goto fail, ทำให้ตัวแปรดัชนีของคุณรั่วไหลลงในขอบเขตการปิดล้อม ฯลฯ ฯลฯ ES6 ได้รับการเข้ารหัสเป็นมาตรฐานด้วยเหตุผลที่ดีและเป็นประโยชน์มากที่สุด บางส่วนง่ายต่อการโพลีฟิลล์ / การแปลงสำหรับเบราว์เซอร์รุ่นเก่า
Jared Smith

5
น่าอ่านมากขึ้น? มันเป็นสิ่งที่แปลกประหลาดสำหรับการวนซ้ำที่ผลักรายการไปยังอาร์เรย์ ไม่ได้รับการอ่านได้มากขึ้นกว่าที่ ......
ฉันปล้ำหมีครั้ง

4
พูดตรงไปตรงมามันรู้สึกเหมือนคุณพยายามอย่างหนักเกินไปที่จะหาสิ่งที่จะบ่นเกี่ยวกับ
ฉันปล้ำหมีครั้งหนึ่ง

2
"ฉันกำลังอ้างถึงข้อผิดพลาด Apple SSL ที่มีชื่อเสียง" - มันไม่ได้มีชื่อเสียงขนาดนั้นที่คุณสามารถอ้างถึงมันในความคิดเห็นแบบสุ่มนอกบริบทและคาดว่าผู้คนจะจดจำมันได้
Hejazzman

14

คุณสามารถรวมmapกับไฟล์filter

var array = [
  {id:123, value:"value1", name:"Name1"},
  {id:124, value:"value2", name:"Name1"},
  {id:125, value:"value3", name:"Name2"},
  {id:126, value:"value4", name:"Name2"}
];

var unique = array
  .map( item => item.name )
  .filter( ( item, idx, arr ) => arr.indexOf( item ) == idx ) 

console.log(unique)


11

คุณสามารถใช้Object.keys ()เพื่อรับอาร์เรย์ของชื่อคุณสมบัติที่แจกแจงได้ของอ็อบเจ็กต์ที่กำหนดเองจากผลลัพธ์อ็อบเจ็กต์ของการวนซ้ำarrayตัวแปรด้วยArray.prototype.reduce ()โดยที่คีย์เป็นชื่อที่ถูกทำลาย

รหัส:

const array = [{id:123, value:"value1", name:"Name1"}, {id:124, value:"value2", name:"Name1"}, {id:125, value:"value3", name:"Name2"}, {id:126, value:"value4", name:"Name2"}],
      names = Object.keys(array.reduce((a, { name }) => (a[name] = 1, a), {}))

console.log(names)


7

คำตอบดีๆมากมายที่นี่ ฉันแค่อยากจะมีส่วนร่วมกับความหลากหลายโดยหวังว่าจะให้มุมมองอื่นแก่คุณ

อาร์เรย์เป็นประเภทออบเจ็กต์ใน JavaScript ดังนั้นจึงสามารถใช้เป็นแฮชได้ในเวลาเดียวกัน ด้วยการใช้ฟังก์ชันนี้เราสามารถลดความซับซ้อนของงานที่ต้องทำในการดำเนินการลดครั้งเดียวด้วยความซับซ้อนของเวลา O (n)

หากคุณไม่พอใจกับอาร์เรย์ของคุณที่มีคุณสมบัติบางอย่างนอกเหนือจากคีย์อาร์เรย์คุณอาจพิจารณาเก็บวัตถุแฮชแยกไว้ด้วย

var array = [{id:123, value:"value1", name:"Name1"},
             {id:124, value:"value2", name:"Name1"},
             {id:125, value:"value3", name:"Name2"},
             {id:126, value:"value4", name:"Name2"}
            ],
result = array.reduce((p,c) => p[c.name] ? p : (p[c.name] = true, p.push(c.name), p), []);
console.log(result);


โซลูชัน ES5 นี้มีประสิทธิภาพที่ดี แต่การใช้อาร์เรย์เพื่อวัตถุประสงค์สองประการนั้นสร้างความสับสนและอันตรายหากชื่อใดชื่อหนึ่งเป็นเพียงตัวเลข ฉันอยากจะแนะนำให้ทำp[c.name]สิ่งต่างๆบนวัตถุที่แยกจากกันoแล้วทิ้งในภายหลัง
joeytwiddle

7

ฉันยอมรับว่าหากคุณต้องการเพียงnameค่านิยม a Setคือหนทางที่จะไป

อย่างไรก็ตามหากคุณต้องการรับอาร์เรย์ของออบเจ็กต์ที่ไม่ซ้ำกันตามnameคุณสมบัติฉันขอแนะนำให้ใช้ไฟล์Map. วิธีที่รวดเร็วในการสร้างแผนที่คือผ่านอาร์เรย์ของ[key, value]อาร์เรย์:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }],
      unique = new Map(array.map(obj => [obj.name, obj]));

// To get the unique objects
const uniques = Array.from(unique.values());

// Get the names like you already did:
console.log("Names:", uniques.map(obj => obj.name));

// If you ever need the complete array of unique objects, you got a ref:
console.log(JSON.stringify(uniques));
.as-console-wrapper { min-height: 100%; }

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



2

ด้วย ES6 สิ่งนี้ควรทำงานได้

var array=[
    {id:123, value:"value1", name:"Name1"},
    {id:124, value:"value2", name:"Name1"},
    {id:125, value:"value3", name:"Name2"},
    {id:126, value:"value4", name:"Name2"}
];

var set = new Set();

array.forEach((a)=>{
    set.add(a.name);
}); 

console.log(Array.from(set));


11
mapควรใช้กับฟังก์ชั่นบริสุทธิ์ไม่ให้มีผลข้างเคียง นี้จะเป็นงานสำหรับหรือforEach reduce
Vincent van der Weele

1

ใช้ UnderscoreJS

array = [{id:123, value:"value1", name:"Name1"}, {id:124, value:"value2", name:"Name1"}, {id:125, value:"value3", name:"Name2"}, {id:126, value:"value4", name:"Name2"}];
get_names =  _.pluck(_.uniq(array, 'name'), 'name')
console.log(get_names)
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

`


0

ใน ES5 ให้ใช้อ็อบเจกต์เป็นพจนานุกรมสำหรับประสิทธิภาพ O ( n )

สิ่งนี้จะใช้ได้ก็ต่อเมื่อคีย์ทั้งหมดเป็น Strings

var array = [
    {id: 123, value: "value1", name: "Name1"},
    {id: 124, value: "value2", name: "Name1"},
    {id: 125, value: "value3", name: "Name2"},
    {id: 126, value: "value4", name: "Name2"}
];

var allNames = array.map(item => item.name);

var map = {};
allNames.forEach(name => {
  map[name] = true;
});
var uniqueNames = Object.keys(map);

console.log(uniqueNames);

คุณสามารถทำสิ่งเดียวกันในนิพจน์เดียวได้หากคุณต้องการ:

var uniqueNames = Object.keys(allNames.reduce((m, n) => (m[n] = true, m), {}));

แต่ฉันพบว่ารูปแบบที่จำเป็นอ่านง่ายกว่า


ฉันใช้ฟังก์ชันลูกศร ES6 เพื่อความชัดเจน หากคุณกำหนดเป้าหมาย ES5 โดยไม่ใช้ทfunction (item) { return item.name; }
รานสไพเลอร์

ในฐานะที่เป็นทางเลือกในการObject.keys(), คำตอบนี้ใช้filter()เพื่อให้เฉพาะชื่อผู้ที่ยังไม่ได้เก็บไว้ในแผนที่ซึ่งเป็นสง่าค่อนข้าง
joeytwiddle


0

ใช้array#forEach()และarray#indexOf()วิธีการเช่นนี้หากคุณต้องการความเข้ากันได้สูงสุดไวยากรณ์ที่กระชับ:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }]

// initialize an empty array named names
let names = [];

// iterate through every element of `array` & check if it's 'name' key's value already in names array if not ADD it 
array.forEach(function(element) { if (names.indexOf(element.name) === -1) names.push(element.name) });
// or use tilde like this:
//array.forEach(function(element) { if (~names.indexOf(element.name)) names.push(element.name) });

console.log(names);

อย่างไรก็ตามหากความเข้ากันได้ไม่ใช่ปัญหาให้ใช้อ็อบเจ็กต์ของECMAScript 6และวิธีการเช่นนี้: Setarray#mapArray.from()

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }];

// iterate through every element from array using map and store it in Set(a Set won't have duplicates) then convert the Set back to Array(using Array.from)
let names = Array.from(new Set(array.map(element => element.name)));

console.log(names);


0

นั่นคือวิธีที่ฉันทำโดยใช้อาร์เรย์ว่างเปล่าแยกต่างหาก

var array = [
   {id:123, value:"value1", name:"Name1"},
   {id:124, value:"value2", name:"Name1"},
   {id:125, value:"value3", name:"Name2"},
   {id:126, value:"value4", name:"Name2"}	    
];

var array2 = []		
		
for (i=0; i<array.length;i++){						
   if (array2.indexOf(array[i].name) == -1){				
     array2.push(array[i].name);
    }
}			

console.log(array2)	


-1

สำหรับผู้ที่ต้องการซับ 1

const names = array.reduce((acc, {name}) => acc.includes(name) ? acc : [name, ...acc], []);

หรือไม่ใช้เมธอดบนต้นแบบของอาร์เรย์

const { reduce, includes } = Array;
const names = reduce(array, (acc, {name}) => includes(acc, name) ? acc : [name, ...acc], []);

อาจเป็นประโยชน์สำหรับการเขียนฟังก์ชันบริสุทธิ์บางอย่างเพื่อจัดการกับสิ่งนี้

const get_uniq_values = (key, arr) => reduce(arr, (a, o) => includes(a, o[key]) ? a : [o[key], ...a], []);

-1
var __array=[{id:123, value:"value1", name:"Name1"},{id:124, value:"value2", name:"Name1"},{id:125, value:"value3", name:"Name2"},{id:126, value:"value4", name:"Name2"}];

function __checkArray(__obj){
    var flag = true;
    for(let i=0; i < __array.length; i++){
        if(__obj.id == __array.id){
            flag = false;
            break;
        }
    }

    return flag;
}

var __valToPush = {id: 127, value: "value5", name: "Name3"};
if(__checkArray(__valToPush)){
    __array.push(__valToPush)
}

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