วิธีผสานสองอาร์เรย์ใน JavaScript และรายการที่ซ้ำกัน


1365

ฉันมีสอง JavaScript อาร์เรย์:

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

ฉันต้องการผลลัพธ์ที่จะเป็น:

var array3 = ["Vijendra","Singh","Shakya"];

อาร์เรย์เอาต์พุตควรลบคำที่ซ้ำออกไป

ฉันจะรวมสองอาร์เรย์ใน JavaScript เพื่อให้ฉันได้รับเฉพาะรายการที่ไม่ซ้ำกันจากแต่ละอาร์เรย์ในลำดับเดียวกันพวกเขาถูกแทรกลงในอาร์เรย์เดิมได้อย่างไร


1
ก่อนที่คุณจะโพสต์คำตอบใหม่ให้พิจารณาว่ามีคำตอบ 75+ คำถามนี้แล้ว กรุณาตรวจสอบให้แน่ใจว่าคำตอบของคุณก่อให้เกิดข้อมูลที่ไม่ใช่คำตอบที่มีอยู่
janniks

[... ชุดใหม่ ([... [1, 2, 3], ... [2, 3, 4]]]] ผล [1, 2, 3, 4]
Denis Giffeler

หากคุณต้องการโซลูชันทั่วไปที่ครอบคลุมการผสานอย่างลึกซึ้งลองดูคำถามนี้แทน บางคำตอบครอบคลุมอาร์เรย์เช่นกัน
Martin Braun

คำตอบ:


1664

เมื่อต้องการรวมอาร์เรย์ (โดยไม่ลบรายการที่ซ้ำกัน)

รุ่น ES5 ใช้Array.concat:

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];

console.log(array1.concat(array2));

รุ่น ES6 ใช้การทำลายล้าง

const array1 = ["Vijendra","Singh"];
const array2 = ["Singh", "Shakya"];
const array3 = [...array1, ...array2];

เนื่องจากไม่มีวิธีการ 'ลบภายใน' เพื่อลบรายการซ้ำ ( จริง ๆ แล้วECMA-262มีArray.forEachประโยชน์อย่างมากสำหรับเรื่องนี้) เราจึงต้องทำด้วยตนเอง:

Array.prototype.unique = function() {
    var a = this.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }

    return a;
};

จากนั้นให้ใช้:

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
// Merges both arrays and gets unique items
var array3 = array1.concat(array2).unique(); 

สิ่งนี้จะรักษาลำดับของอาร์เรย์ (เช่นไม่จำเป็นต้องเรียงลำดับ)

เนื่องจากผู้คนจำนวนมากรู้สึกรำคาญกับการเติมArray.prototypeและfor inลูปต้นแบบที่นี่จึงเป็นวิธีการบุกรุกที่น้อยกว่าในการใช้งาน:

function arrayUnique(array) {
    var a = array.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }

    return a;
}

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
    // Merges both arrays and gets unique items
var array3 = arrayUnique(array1.concat(array2));

สำหรับผู้ที่โชคดีพอที่จะทำงานกับเบราว์เซอร์ที่มี ES5 ให้ใช้งานคุณสามารถใช้Object.definePropertyสิ่งนี้:

Object.defineProperty(Array.prototype, 'unique', {
    enumerable: false,
    configurable: false,
    writable: false,
    value: function() {
        var a = this.concat();
        for(var i=0; i<a.length; ++i) {
            for(var j=i+1; j<a.length; ++j) {
                if(a[i] === a[j])
                    a.splice(j--, 1);
            }
        }

        return a;
    }
});

281
โปรดทราบว่าอัลกอริทึมนี้คือ O (n ^ 2)
Gumbo

7
อนุญาต[a, b, c]และ[x, b, d]เป็นอาร์เรย์ (สมมติคำพูด) concat [a, b, c, x, b, d]ให้ การส่งออกของที่ไม่ซ้ำกัน () จะ[a, c, x, b, d]ไม่ ที่ไม่รักษาคำสั่งที่ฉันคิดว่า - ฉันเชื่อว่าต้องการ OP[a, b, c, x, d]
Amarghosh

89
OP ยอมรับคำตอบแรกที่ทำให้เขาทำงานและลงชื่อดูเหมือนว่า เรายังคงเปรียบเทียบกับโซลูชั่นของแต่ละคนในการหา-N-การแก้ไขความผิดพลาดในการปรับปรุงประสิทธิภาพการทำให้แน่ใจว่ามันเข้ากันได้ทุกที่และอื่น ๆ ... ความงามของ StackOverflow :-)
Amarghosh

6
ฉันได้รับการโหวตครั้งนี้ แต่เปลี่ยนใจ การกำหนดต้นแบบให้กับ Array.prototype มีผลที่ตามมาของการแตกคำสั่ง "for ... in" ดังนั้นทางออกที่ดีที่สุดน่าจะใช้ฟังก์ชั่นเช่นนี้ แต่ไม่ได้กำหนดให้เป็นแบบตัวอย่าง บางคนอาจโต้แย้งว่าคำสั่ง "for ... in" ไม่ควรใช้เพื่อทำซ้ำองค์ประกอบของอาร์เรย์อย่างไรก็ตามผู้คนมักจะใช้วิธีดังกล่าวดังนั้นอย่างน้อยที่สุดวิธีนี้จึงควรใช้ด้วยความระมัดระวัง
ผู้บัญชาการรหัส

16
คุณควรใช้เสมอfor ... inด้วยhasOwnPropertyซึ่งในกรณีวิธีการต้นแบบดี
mulllhausen

600

ด้วย Underscore.js หรือ Lo-Dash คุณสามารถทำได้:

console.log(_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

http://underscorejs.org/#union

http://lodash.com/docs#union


70
หรือบางทีอาจจะดียิ่งขึ้นกว่าขีด API ที่ได้lodash
Brian M. Hunt

3
@Ygg จากเอกสาร lodash "ส่งคืนอาร์เรย์ของค่าที่ไม่ซ้ำกันใหม่ตามลำดับที่มีอยู่ในหนึ่งหรือหลายอาร์เรย์"
Richard Ayotte

4
ฉันชอบ underscore.js สิ่งที่ฉันลงเอยด้วยการใช้คือunderscore.flatten()ซึ่งดีกว่าสหภาพในการที่ใช้อาร์เรย์
ผู้ประกอบ

8
@weaver _.flatten ผสาน แต่ไม่ได้ 'ทำซ้ำ'
GijsjanB

9
ประสิทธิภาพที่รวดเร็วใช้กับบ้านพักและคำตอบยอดนิยม: jsperf.com/merge-two-arrays-keeping-only-unique-values
slickplaid

288

ก่อนเชื่อมทั้งสองอาร์เรย์ต่อไปกรองเฉพาะรายการที่ไม่ซ้ำกัน:

var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b)
var d = c.filter((item, pos) => c.indexOf(item) === pos)

console.log(d) // d is [1, 2, 3, 101, 10]

แก้ไข

ดังที่แนะนำวิธีแก้ปัญหาที่ชาญฉลาดกว่าคือการกรองรายการที่ไม่ซ้ำกันbก่อนที่จะต่อกับa:

var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b.filter((item) => a.indexOf(item) < 0))

console.log(c) // c is [1, 2, 3, 101, 10]


5
โซลูชันดั้งเดิมที่นี่มีประโยชน์ในการลบรายการที่ซ้ำภายในแต่ละอาร์เรย์ของแหล่งข้อมูล ฉันเดาว่ามันขึ้นอยู่กับบริบทของคุณที่คุณจะใช้
theGecko

คุณสามารถผสานที่แตกต่างกันสำหรับการสนับสนุน IE6: c = Array.from (ชุดใหม่ (c));
Tobi G.

หากฉันต้องการเปลี่ยนaเพื่อเพิ่มจริง ๆbแล้วมันจะดีกว่าที่จะวนซ้ำและใช้การพุชหรือไม่ a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
ความกลัว

1
เพียงเตือนความทรงจำของการใช้เบราว์เซอร์ปัจจุบันcaniuse.com/usage-tableสำหรับผู้ที่กังวลเกี่ยวกับ IE6
pmrotule

10
@Andrew: ดียิ่งขึ้น: 1. var c = [...a, ...b.filter(o => !~a.indexOf(o))]; 2. var c = [...new Set([...a, ...b])];
7vujy0f0hy

203

นี่คือโซลูชัน ECMAScript 6 ที่ใช้ตัวดำเนินการสเปรดและอาเรย์ทั่วไป

ขณะนี้มันใช้งานได้กับ Firefox เท่านั้นและอาจเป็นตัวอย่างทางเทคนิคของ Internet Explorer

แต่ถ้าคุณใช้Babelคุณสามารถทำได้ทันที

const input = [
  [1, 2, 3],
  [101, 2, 1, 10],
  [2, 1]
];
const mergeDedupe = (arr) => {
  return [...new Set([].concat(...arr))];
}

console.log('output', mergeDedupe(input));


14
ควรเพิ่มคำตอบที่ได้รับการยอมรับ โซลูชันนี้มีประสิทธิภาพมากขึ้นและสวยงามกว่าสิ่งที่เป็นไปได้ในปัจจุบัน แต่เป็นสิ่งที่เราจะสามารถทำได้อย่างหลีกเลี่ยงไม่ได้ (และควรทำเพื่อให้ทันในสาขานี้)
EmmaGamma

นี่ไม่ใช่คำถามเดียวกันกับคำถามของ OP (นี่น่าจะเป็น flatmap มากกว่าสิ่งใด ๆ ) แต่มี upvote เพราะมันยอดเยี่ยม
jedd.ahyoung

4
ยากที่จะพูดว่านี่ควรเป็นคำตอบที่ได้รับการยอมรับเนื่องจากคำถามนี้มาจากปี 2009 แต่ใช่ไม่ใช่แค่ "นักแสดง" เท่านั้น แต่ยังรวมถึง "สง่างาม" อีกด้วย
Cezar Augusto

11
Array.fromสามารถใช้แทนตัวดำเนินการแพร่กระจาย: Array.from(new Set([].concat(...arr)))
Henry Blyth

1
นี่คือสง่างามมาก น่าเสียดายที่ typescript ยังไม่รองรับสิ่งนี้ stackoverflow.com/questions/33464504/…
Ben Carp

190

ES6

array1.push(...array2) // => don't remove duplication 

หรือ

[...array1,...array2] //   =>  don't remove duplication 

หรือ

[...new Set([...array1 ,...array2])]; //   => remove duplication

1
1 / ตัวอย่างที่ 2 ไม่มีunionที่ทุกคนตัดสินยกตัวอย่างเช่น + 1 ขึ้นสแต็คขนาดใหญ่Arrays + ตัวอย่างที่ 3 ช้าอย่างไม่น่าเชื่อและสิ้นเปลืองหน่วยความจำมากตั้งแต่สองกลางArrays จะต้องมีการสร้าง + ตัวอย่างที่ 3 สามารถนำมาใช้เฉพาะunionกับการที่เป็นที่รู้จัก จำนวนArrays ณ เวลารวบรวม

แล้วคุณจะทำอย่างไร
David Noreña

14
Setเป็นวิธีที่จะไปที่นี่
philk

3
โปรดทราบว่าสำหรับชุดไม่สามารถทำซ้ำสองวัตถุที่มีคู่ค่าคีย์เดียวกันยกเว้นว่าเป็นการอ้างอิงวัตถุเดียวกัน
Jun711

2
ไม่ทำงานกับอาร์เรย์ของวัตถุเพราะมันจะรวมการอ้างอิงวัตถุเท่านั้นและไม่สนใจว่าวัตถุตัวเองเท่ากัน
วิลสันบิ๊กส์

87

การใช้ชุด (ECMAScript 2015) จะง่ายพอ ๆ กับที่:

const array1 = ["Vijendra", "Singh"];
const array2 = ["Singh", "Shakya"];
console.log(Array.from(new Set(array1.concat(array2))));


7
ฉันพิจารณาสิ่งนี้ "คำตอบที่ยอมรับ" สำหรับการใช้ ES6
mwieczorek

9
@mwieczorek วิธีการเกี่ยวกับ:const array3 = [...new Set(array1.concat(array2))]
Robby Cornelissen

5
ไม่ทำงานหากคุณใช้ Array of objects
carkod

1
สำหรับการรวมวัตถุต่าง ๆ โดยไม่ทำซ้ำ: stackoverflow.com/a/54134237/3131433
Rakibul Haq

38

นี่คือความแตกต่างเล็กน้อยในวง ด้วยการปรับปรุงบางอย่างใน Chrome รุ่นล่าสุดจึงเป็นวิธีที่เร็วที่สุดในการแก้ไขการรวมกันของสองอาร์เรย์ (Chrome 38.0.2111)

http://jsperf.com/merge-two-arrays-keeping-only-unique-values

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [];

var arr = array1.concat(array2),
  len = arr.length;

while (len--) {
  var itm = arr[len];
  if (array3.indexOf(itm) === -1) {
    array3.unshift(itm);
  }
}

ในขณะที่ลูป: ~ 589k ops / s
ตัวกรอง: ~ 445k ops / s
lodash: 308k ops / s
สำหรับลูป: 225k ops / s

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

http://jsperf.com/merge-two-arrays-keeping-only-unique-values/52

let whileLoopAlt = function (array1, array2) {
    const array3 = array1.slice(0);
    let len1 = array1.length;
    let len2 = array2.length;
    const assoc = {};

    while (len1--) {
        assoc[array1[len1]] = null;
    }

    while (len2--) {
        let itm = array2[len2];

        if (assoc[itm] === undefined) { // Eliminate the indexOf call
            array3.push(itm);
            assoc[itm] = null;
        }
    }

    return array3;
};

ในโซลูชันทางเลือกนี้ฉันได้รวมโซลูชันอาเรย์แบบเชื่อมโยงของคำตอบหนึ่งคำตอบเพื่อตัดการ.indexOf()โทรในลูปซึ่งทำให้สิ่งต่าง ๆ ช้าลงมากด้วยลูปที่สองและรวมการเพิ่มประสิทธิภาพอื่น ๆ ที่ผู้ใช้คนอื่น ๆ .

คำตอบยอดนิยมที่นี่พร้อมลูปสองครั้งในทุกค่า (i-1) ยังช้ากว่าอย่างมาก lodash ยังคงทำงานได้ดีและฉันยังคงแนะนำให้กับทุกคนที่ไม่สนใจที่จะเพิ่มห้องสมุดในโครงการของพวกเขา สำหรับผู้ที่ไม่ต้องการห่วงของฉันในขณะที่ยังคงเป็นคำตอบที่ดีและคำตอบตัวกรองมีการแสดงที่แข็งแกร่งมากที่นี่เต้นออกทั้งหมดในการทดสอบของฉันกับ Canary Chrome ล่าสุด (44.0.2360) ขณะที่เขียนนี้

ตรวจสอบคำตอบของ MikeและคำตอบของDan Stockerหากคุณต้องการเพิ่มความเร็วให้สูงขึ้น สิ่งเหล่านี้เป็นผลลัพธ์ที่เร็วที่สุดหลังจากผ่านคำตอบที่ใช้ได้จริงเกือบทั้งหมด


มีข้อบกพร่องในวิธีการของคุณ: คุณใส่การสร้าง array3 ในขั้นตอนการติดตั้งในขณะที่ค่าใช้จ่ายนั้นควรเป็นส่วนหนึ่งของคะแนนโซลูชันในขณะที่คุณใช้ เมื่อมีการย้าย 1 บรรทัดโซลูชันของคุณจะมีความเร็วเท่ากับลูปที่อิง ฉันเข้าใจว่าอาเรย์นั้นสามารถนำกลับมาใช้ใหม่ได้ แต่อัลกอริทึมอื่น ๆ อาจได้รับประโยชน์เช่นกันโดยไม่ต้องประกาศและเตรียมใช้งาน Building Block ที่จำเป็นทั้งหมด
doldt

ฉันเห็นด้วยกับหลักฐานของคุณ @doldt แต่ไม่เห็นด้วยกับผลลัพธ์ของคุณ มีข้อบกพร่องในการออกแบบขั้นพื้นฐานที่มีการลบรายการแบบวนซ้ำซึ่งคุณจะต้องตรวจสอบความยาวของอาร์เรย์อีกครั้งหลังจากที่คุณลบรายการออกทำให้การดำเนินการช้าลง A ขณะที่ลูปทำงานไปข้างหลังไม่มีผลกระทบเหล่านี้ นี่คือตัวอย่างที่มีการลบตัวแปรการตั้งค่ามากที่สุดเท่าที่จะทำได้โดยไม่เปลี่ยนคำตอบเดิมมากเกินไป: jsperf.com/merge-two-arrays-keeping-only-unique-values/19
slickplaid

@slickplaid การทดสอบที่เชื่อมโยงนั้นว่างเปล่าและการแก้ไขครั้งต่อไปที่ jsperf ค้างในขณะที่ลูป
doldt

@doldt ฉันได้แจ้งข้อกังวลของคุณในคำตอบของฉันแล้วและเพิ่มการทดสอบที่อัปเดตที่เหมาะสมให้กับมันด้วย แจ้งให้เราทราบหากคุณเห็นด้วยกับผลลัพธ์เหล่านั้นหรือไม่ นอกจากนี้ฉันเพิ่มอีกผลลัพธ์ที่ดีกว่าโดยใช้อาเรย์แบบเชื่อมโยง
slickplaid

1
@slickplaid ขอบคุณที่ตั้งค่าหน้าเพอร์ขยาย ฟังก์ชั่น "whileLoopAlt2" จะหายไปถ้าฉันไม่มีอะไรหายไป มันสร้างอาร์เรย์ใหม่ที่มีอาร์เรย์แรกและอาร์เรย์ที่สอง (ในลำดับที่กลับกัน) เพื่อหลีกเลี่ยงความสับสนฉันได้ทำการแก้ไขอีกครั้งที่ลบฟังก์ชั่นที่เสียหาย ฉันยังเพิ่มตัวอย่างเพิ่มเติม: jsperf.com/merge-two-arrays-keeping-only-unique-values/22
Stephen S

37

คุณสามารถทำได้ง่ายๆด้วย ECMAScript 6

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
  • ใช้โอเปอเรเตอร์การกระจายเพื่อต่ออาร์เรย์
  • ใช้Setเพื่อสร้างชุดองค์ประกอบที่แตกต่าง
  • ใช้ตัวดำเนินการสเปรดอีกครั้งเพื่อแปลง Set เป็นอาร์เรย์

2
ฉันได้รับข้อผิดพลาด: Type 'Set <string>' ไม่ใช่ประเภทอาร์เรย์
gattsbr

3
Array.from(new Set(array1.concat(array2)))และถ้าคุณมีเหตุผลบางอย่างไม่ต้องการที่จะใช้ประกอบการแพร่กระจายนอกจากนี้ยังมี:
kba

@gattsbr กับ typescript ในtsconfig.jsonคุณสามารถเพิ่มการ"downlevelIteration": true compilerOptions
VincentPerrin.com

19
Array.prototype.merge = function(/* variable number of arrays */){
    for(var i = 0; i < arguments.length; i++){
        var array = arguments[i];
        for(var j = 0; j < array.length; j++){
            if(this.indexOf(array[j]) === -1) {
                this.push(array[j]);
            }
        }
    }
    return this;
};

ฟังก์ชันการรวมอาร์เรย์ที่ดีขึ้นมาก


4
var test = ['a', 'b', 'c']; console.log(test); จะพิมพ์ ["a", "b", "c", merge: function]
Doubidou

ทางออกที่ดีเยี่ยม ฉันได้อัปเดตการทดสอบ jsperf ที่โพสต์ข้างต้นโดย @slickplaid ( jsperf.com/merge-two-arrays-keeping-only-unique-values/3 ) และดูเหมือนว่านี่เป็นหนึ่งในวิธีที่เร็วที่สุด
คอบร้า

@Cobra ความเสี่ยงของการทำให้เกิดเสียงเรียกเข้าเล็กน้อยทำงานบน Chrome 40.0.2214 (ล่าสุดจนถึง 2/18/15) คำตอบนี้ช้ากว่าของฉัน 53% OTOH IE11 ดูเหมือนจะไม่เหมาะสำหรับคำตอบของฉันเลย :) มือถือ Chrome ยังคงโยกมันอยู่ สุจริตถ้าคุณกำลังใช้ lodash / _ ที่เราส่วนใหญ่ควรคำตอบที่แท้จริงนั้นค่อนข้างสูงในรายการนี้ :)
slickplaid

@slickplaid True และค่อนข้างเร็วขึ้นแม้จะเปรียบเทียบกับ lodash / _ one ฉันอาจจะสิ้นสุดการสลับการใช้งานของฉัน ณ จุดหนึ่งหรือบางสิ่งที่คล้ายกับของคุณ : D
Cobra

1
ไม่แน่ใจว่าค่าใช้จ่ายของวิธี indexOf () คืออะไร แต่นี่อาจเป็นวิธีที่เข้ากันได้กับ ES5 ที่เร็วที่สุด ยังไม่มีค่าใด ๆ ที่ความยาวของตัวแปรของอาร์กิวเมนต์ไม่จำเป็น วิธีนี้สามารถเชนได้ @slickplaid การโหลดห้องสมุดไม่ใช่คำตอบสำหรับคำถาม "วิธีทำใน javascript" แน่นอนว่าห้องสมุดหลายแห่งมีฟังก์ชั่นในการทำงานให้เสร็จ 7 บรรทัด
dehart

19

เพียงแค่โยนสองเซ็นต์ของฉัน

function mergeStringArrays(a, b){
    var hash = {};
    var ret = [];

    for(var i=0; i < a.length; i++){
        var e = a[i];
        if (!hash[e]){
            hash[e] = true;
            ret.push(e);
        }
    }

    for(var i=0; i < b.length; i++){
        var e = b[i];
        if (!hash[e]){
            hash[e] = true;
            ret.push(e);
        }
    }

    return ret;
}

นี่เป็นวิธีที่ฉันใช้บ่อย ๆ มันใช้วัตถุเป็นตาราง hashlookup เพื่อทำการตรวจสอบซ้ำ สมมติว่า hash คือ O (1) ดังนั้นสิ่งนี้จะทำงานใน O (n) โดยที่ n คือ a.length + b.length ฉันไม่รู้ว่าเบราว์เซอร์ทำแฮชอย่างไร แต่มันทำงานได้ดีกับจุดข้อมูลหลายพันจุด


ทำได้ดีมาก เอาชนะผลลัพธ์อื่น ๆ ในหน้านี้ได้ค่อนข้างมาก (ถ้าไม่ทั้งหมด) โดยใช้ประโยชน์จากอาเรย์แบบเชื่อมโยงและป้องกันลูปของ indexOf และการดำเนินการอื่น ๆ jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid

"hash" ของคุณเป็นString()ฟังก์ชั่นใน javascript ซึ่งอาจใช้ได้กับค่าดั้งเดิม (แม้ว่าจะมีการชนกันระหว่างประเภท) แต่ก็ไม่เหมาะสำหรับอาร์เรย์ของวัตถุ
Bergi

ฉันใช้โซลูชันที่คล้ายกันฉันอนุญาตให้ส่งผ่านฟังก์ชัน hashCode หรือส่งผ่านสตริงเพื่อระบุคุณสมบัติในวัตถุเพื่อใช้เป็นคีย์แฮช
Robert Baker

19

เพียงคัดท้ายชัดเจนของลูปซ้อนกัน (O (n ^ 2)) และ.indexOf()(+ O (n))

function merge(a, b) {
    var hash = {}, i;
    for (i=0; i<a.length; i++) {
        hash[a[i]]=true;
    } 
    for (i=0; i<b.length; i++) {
        hash[b[i]]=true;
    } 
    return Object.keys(hash);
}

2
ช่างน่าทึ่งมากโดยเฉพาะอย่างยิ่งถ้าคุณทำสาย ตัวเลขจะต้องมีขั้นตอนเพิ่มเติมเพื่อให้เป็นเช่นนี้ ฟังก์ชั่นนี้เต้นหนักตัวเลือกอื่น ๆ ทั้งหมดถ้าคุณไม่สนใจ (หรือสนใจ) ว่าทุกอย่างเป็นสตริงหลังจากคุณทำเสร็จแล้ว งานที่ดี. ผลการปฏิบัติงานที่นี่: jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid

18

แบบง่ายที่สุดของคำตอบนี้และเปลี่ยนเป็นฟังก์ชั่นที่ดี:

function mergeUnique(arr1, arr2){
    return arr1.concat(arr2.filter(function (item) {
        return arr1.indexOf(item) === -1;
    }));
}

2
ฉันเชื่อว่านี่สะอาดกว่าคำตอบที่ยอมรับ ดูเหมือนว่าตัวกรองได้รับการสนับสนุนใน ECMAScript 5.1 + ซึ่งตอนนี้ได้รับการสนับสนุนแล้ว
Tom Fobear

สิ่งนี้ควรได้รับการยอมรับ
wallop

นี่คือรวบรัดมากขึ้น
Mox

15

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

var set1 = {"Vijendra":true, "Singh":true}
var set2 = {"Singh":true,  "Shakya":true}

// Merge second object into first
function merge(set1, set2){
  for (var key in set2){
    if (set2.hasOwnProperty(key))
      set1[key] = set2[key]
  }
  return set1
}

merge(set1, set2)

// Create set from array
function setify(array){
  var result = {}
  for (var item in array){
    if (array.hasOwnProperty(item))
      result[array[item]] = true
  }
  return result
}

คุณไม่หมายถึงif (!set1.hasOwnProperty(key))เหรอ
Gumbo

2
ทำไมฉันต้องหมายความว่าอย่างนั้น? วัตถุประสงค์ของเงื่อนไขนั้นคือการเพิกเฉยต่อคุณสมบัติที่อาจอยู่ในต้นแบบของวัตถุ
Nick Retallack

12

ทางออกที่ดีที่สุด ...

คุณสามารถตรวจสอบโดยตรงในคอนโซลของเบราว์เซอร์โดยการกดปุ่ม ...

ไม่ซ้ำซ้อน

a = [1, 2, 3];
b = [3, 2, 1, "prince"];

a.concat(b.filter(function(el) {
    return a.indexOf(el) === -1;
}));

ด้วยซ้ำ

["prince", "asish", 5].concat(["ravi", 4])

หากคุณต้องการโดยไม่ซ้ำกันคุณสามารถลองวิธีการแก้ปัญหาที่ดีขึ้นจากที่นี่ - ตะโกนรหัส

[1, 2, 3].concat([3, 2, 1, "prince"].filter(function(el) {
    return [1, 2, 3].indexOf(el) === -1;
}));

ลองใช้คอนโซลเบราว์เซอร์ Chrome

 f12 > console

เอาท์พุท:

["prince", "asish", 5, "ravi", 4]

[1, 2, 3, "prince"]

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

9

เงินครึ่งหนึ่งของฉัน:

Array.prototype.concat_n_dedupe = function(other_array) {
  return this
    .concat(other_array) // add second
    .reduce(function(uniques, item) { // dedupe all
      if (uniques.indexOf(item) == -1) {
        uniques.push(item);
      }
      return uniques;
    }, []);
};

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var result = array1.concat_n_dedupe(array2);

console.log(result);

มันไม่ได้ใช้อะไรที่ใหม่ใน ES6 ฉันคิดถึงอะไรบางอย่างเหรอ?
Bergi

@Bergi: ใช่คุณพูดถูก ขอบคุณที่สังเกต อย่างใดฉันก็เล่นกับสคริปต์นี้และอาจมีบางรุ่นที่มีฟังก์ชั่น ES6 แต่ตอนนี้มันมี indexOf ซึ่งมีมานานหลายศตวรรษ ความผิดพลาดของฉันขอโทษ
Hero Qu

8

คุณสามารถทำได้โดยใช้ Underscore.js's => uniq :

array3 = _.uniq(array1.concat(array2))

console.log(array3)

มันจะพิมพ์[ "Vijendra", "ซิงห์", "Shakya"]



7

ฉันรู้ว่าคำถามนี้ไม่ได้เกี่ยวกับอาร์เรย์ของวัตถุ แต่ผู้ค้นหาทำที่นี่

ดังนั้นจึงเป็นการเพิ่มมูลค่าให้กับผู้อ่านในอนาคตด้วยวิธีการรวม ES6 ที่เหมาะสมจากนั้นจึงลบข้อมูลที่ซ้ำกัน

อาร์เรย์ของวัตถุ :

var arr1 = [ {a: 1}, {a: 2}, {a: 3} ];
var arr2 = [ {a: 1}, {a: 2}, {a: 4} ];

var arr3 = arr1.concat(arr2.filter( ({a}) => !arr1.find(f => f.a == a) ));

// [ {a: 1}, {a: 2}, {a: 3}, {a: 4} ]

6
//Array.indexOf was introduced in javascript 1.6 (ECMA-262) 
//We need to implement it explicitly for other browsers, 
if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt, from)
  {
    var len = this.length >>> 0;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}
//now, on to the problem

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var merged = array1.concat(array2);
var t;
for(i = 0; i < merged.length; i++)
  if((t = merged.indexOf(i + 1, merged[i])) != -1)
  {
    merged.splice(t, 1);
    i--;//in case of multiple occurrences
  }

การใช้indexOfวิธีการสำหรับเบราว์เซอร์อื่นนำมาจากMDC


1
ฉันหามันไม่เจอในโรงเรียน w3 นั่นเป็นเหตุผลที่ฉันเขียนมัน w3schools.com/jsref/jsref_obj_array.aspใช้fromพารามิเตอร์ btw หรือไม่
Amarghosh

ขอบคุณ @Gumbo และ @meder - ฉันจะเปลี่ยนบุ๊กมาร์กของฉันตอนนี้ ฉันยังไม่ได้ทำอะไรจริงจังใน js และฉันใช้ w3schools สำหรับการอ้างอิงแบบไม่เป็นทางการ (นั่นคือทั้งหมดที่ฉันต้องการ) - อาจเป็นเหตุผลที่ฉันไม่ได้ตระหนักถึงสิ่งนั้น
Amarghosh

MDC กล่าวว่า indexOf ต้องการ javascript 1.6 จะปลอดภัยหรือไม่ถ้าสมมติว่าเบราว์เซอร์ทั่วไป (> = FF2,> IE6 เป็นต้น) จะรองรับหรือไม่
Amarghosh

4
IE6 ไม่สนับสนุน Array.prototype.indexOf เพียงวางวิธีการสนับสนุนที่ได้รับจาก Mozilla ดังนั้น IE จะไม่เกิดข้อผิดพลาด
meder omuraliev

indexOfการปรับปรุงการใช้ ทำความสะอาดรหัสโดยการลบส่วนที่แสดงความคิดเห็น @meder - ขอบคุณอีกครั้ง
Amarghosh

6

โซลูชั่นใหม่ (ซึ่งใช้Array.prototype.indexOfและArray.prototype.concat):

Array.prototype.uniqueMerge = function( a ) {
    for ( var nonDuplicates = [], i = 0, l = a.length; i<l; ++i ) {
        if ( this.indexOf( a[i] ) === -1 ) {
            nonDuplicates.push( a[i] );
        }
    }
    return this.concat( nonDuplicates )
};

การใช้งาน:

>>> ['Vijendra', 'Singh'].uniqueMerge(['Singh', 'Shakya'])
["Vijendra", "Singh", "Shakya"]

Array.prototype.indexOf (สำหรับ internet explorer):

Array.prototype.indexOf = Array.prototype.indexOf || function(elt)
  {
    var len = this.length >>> 0;

    var from = Number(arguments[1]) || 0;
    from = (from < 0) ? Math.ceil(from): Math.floor(from); 
    if (from < 0)from += len;

    for (; from < len; from++)
    {
      if (from in this && this[from] === elt)return from;
    }
    return -1;
  };

@ ผู้ส่ง: หากคำสั่งซื้อไม่สำคัญแล้วฉันจะทำสิ่งนี้ได้อย่างไร
Vijjendra

1
ไม่ใช่วิธี ECMAScript มาตรฐานที่กำหนดไว้สำหรับ Array.prototype แต่ฉันรู้ว่าคุณสามารถกำหนดได้อย่างง่ายดายสำหรับ IE และเบราว์เซอร์อื่น ๆ ที่ไม่สนับสนุน
meder omuraliev

โปรดทราบว่าอัลกอริทึมนี้คือ O (n ^ 2)
Gumbo

อัลกอริทึมของคุณคืออะไร
meder omuraliev

@meder: อัลกอริทึมของฉันเป็นอัลกอริทึมแบบสหภาพ การรวมกันนั้นทำใน O (n + m) แต่การเรียงลำดับใช้เวลามากที่สุด O (n · log n + m · log m) ดังนั้นอัลกอริทึมทั้งหมดคือ O (n · log n + m · log m)
Gumbo

6

มันสามารถทำได้โดยใช้ชุด

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var array3 = array1.concat(array2);
var tempSet = new Set(array3);
array3 = Array.from(tempSet);

//show output
document.body.querySelector("div").innerHTML = JSON.stringify(array3);
<div style="width:100%;height:4rem;line-height:4rem;background-color:steelblue;color:#DDD;text-align:center;font-family:Calibri" > 
  temp text 
</div>


6

มีวิธีแก้ปัญหามากมายสำหรับการรวมสองอาร์เรย์ พวกเขาสามารถแบ่งออกเป็นสองประเภทหลัก (ยกเว้นการใช้ห้องสมุดบุคคลที่สามเช่น lodash หรือ underscore.js)

a) รวมสองอาร์เรย์และลบรายการที่ซ้ำกัน

b) กรองรายการก่อนรวมเข้าด้วยกัน

รวมสองอาร์เรย์และลบรายการที่ซ้ำกัน

รวม

// mutable operation(array1 is the combined array)
array1.push(...array2);
array1.unshift(...array2);

// immutable operation
const combined = array1.concat(array2);
const combined = [...array1, ...array2];    // ES6

รวม

มีหลายวิธีในการรวมอาเรย์เข้าด้วยกันฉันขอแนะนำวิธีการด้านล่างสองวิธี

// a little bit tricky
const merged = combined.filter((item, index) => combined.indexOf(item) === index);
const merged = [...new Set(combined)];

กรองรายการก่อนรวมเข้าด้วยกัน

นอกจากนี้ยังมีหลายวิธี แต่โดยส่วนตัวฉันแนะนำรหัสด้านล่างเนื่องจากความเรียบง่าย

const merged = array1.concat(array2.filter(secItem => !array1.includes(secItem)));

5
Array.prototype.add = function(b){
    var a = this.concat();                // clone current object
    if(!b.push || !b.length) return a;    // if b is not an array, or empty, then return a unchanged
    if(!a.length) return b.concat();      // if original is empty, return b

    // go through all the elements of b
    for(var i = 0; i < b.length; i++){
        // if b's value is not in a, then add it
        if(a.indexOf(b[i]) == -1) a.push(b[i]);
    }
    return a;
}

// Example:
console.log([1,2,3].add([3, 4, 5])); // will output [1, 2, 3, 4, 5]

5
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)

สิ่งที่ดีเกี่ยวกับอันนี้คือประสิทธิภาพการทำงานและคุณโดยทั่วไปเมื่อทำงานกับอาร์เรย์มีวิธีการผูกมัดเช่นตัวกรองแผนที่ ฯลฯ เพื่อให้คุณสามารถเพิ่มบรรทัดนั้นและมันจะ concat และ deduplicate array2 กับ array1 โดยไม่จำเป็นต้องอ้างอิงในภายหลัง หนึ่ง (เมื่อคุณกำลังผูกมัดวิธีที่คุณไม่มี) ตัวอย่าง:

someSource()
.reduce(...)
.filter(...)
.map(...) 
// and now you want to concat array2 and deduplicate:
.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)
// and keep chaining stuff
.map(...)
.find(...)
// etc

(ฉันไม่ชอบที่จะทำให้มลภาวะ Array.prototype และนั่นเป็นวิธีเดียวในการเคารพห่วงโซ่ - การกำหนดฟังก์ชั่นใหม่จะทำลายมัน - ดังนั้นฉันคิดว่าสิ่งนี้เป็นวิธีเดียวที่จะทำให้สำเร็จ)



3

วิธีการทำงานกับ ES2015

ต่อไปนี้วิธีการทำงานunionของทั้งสองArrays เป็นเพียงองค์ประกอบของและconcat filterเพื่อให้มีประสิทธิภาพสูงสุดเราจึงเลือกใช้Setประเภทข้อมูลดั้งเดิมซึ่งปรับให้เหมาะสมกับการค้นหาคุณสมบัติ

อย่างไรก็ตามคำถามที่สำคัญร่วมกับunionฟังก์ชั่นคือวิธีการรักษาที่ซ้ำกัน การเปลี่ยนลำดับต่อไปนี้เป็นไปได้:

Array A      + Array B

[unique]     + [unique]
[duplicated] + [unique]
[unique]     + [duplicated]
[duplicated] + [duplicated]

การเปลี่ยนลำดับสองครั้งแรกนั้นง่ายต่อการจัดการด้วยฟังก์ชั่นเดียว อย่างไรก็ตามสองรายการสุดท้ายนั้นซับซ้อนกว่าเนื่องจากคุณไม่สามารถประมวลผลได้ตราบใดที่คุณต้องพึ่งพาการSetค้นหา เนื่องจากการเปลี่ยนเป็นการObjectค้นหาคุณสมบัติแบบเก่าแบบธรรมดาจะส่งผลให้เกิดประสิทธิภาพการทำงานที่รุนแรงการดำเนินการดังต่อไปนี้เพียงเพิกเฉยต่อการเปลี่ยนแปลงที่สามและสี่ คุณจะต้องสร้างเวอร์ชันแยกต่างหากunionเพื่อสนับสนุน


// small, reusable auxiliary functions

const comp = f => g => x => f(g(x));
const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// de-duplication

const dedupe = comp(afrom) (createSet);


// the actual union function

const union = xs => ys => {
  const zs = createSet(xs);  
  return concat(xs) (
    filter(x => zs.has(x)
     ? false
     : zs.add(x)
  ) (ys));
}


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,4,5,6,6];


// here we go

console.log( "unique/unique", union(dedupe(xs)) (ys) );
console.log( "duplicated/unique", union(xs) (ys) );

จากที่นี่ไปมันเป็นเรื่องไม่สำคัญที่จะใช้unionnฟังก์ชั่นซึ่งยอมรับจำนวนของอาร์เรย์ใด ๆ (แรงบันดาลใจจากความคิดเห็นของ naomik):

// small, reusable auxiliary functions

const uncurry = f => (a, b) => f(a) (b);
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);

const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// union and unionn

const union = xs => ys => {
  const zs = createSet(xs);  
  return concat(xs) (
    filter(x => zs.has(x)
     ? false
     : zs.add(x)
  ) (ys));
}

const unionn = (head, ...tail) => foldl(union) (head) (tail);


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,4,5,6,6];
const zs = [0,1,2,3,4,5,6,7,8,9];


// here we go

console.log( unionn(xs, ys, zs) );

ปรากฎว่าunionnเป็นเพียงfoldl(aka Array.prototype.reduce) ซึ่งใช้unionเป็นตัวลด หมายเหตุ: เนื่องจากการใช้งานไม่ได้ใช้ตัวสะสมเพิ่มเติมมันจะโยนข้อผิดพลาดเมื่อคุณใช้โดยไม่มีข้อโต้แย้ง


1
ความคิดเห็นสองข้อ: ฉันสังเกตเห็นflipและnotfไม่ได้ใช้ รวมunionByถึงรายละเอียดการดำเนินการรั่วไหล (ต้องมีความรู้โดยนัยของSetประเภท) มันอาจจะดีถ้าคุณเพียงแค่จะทำอะไรเช่นนี้และunion = unionBy (apply) unionci = unionBy (p => x => p(x.toLowerCase()))ด้วยวิธีการที่ผู้ใช้เพียงแค่ส่งสิ่งที่มีค่าการจัดกลุ่มp- เพียงแค่ความคิด ^ _ ^
ขอบคุณ

zsประกาศตัวแปรยังขาดvar/ letคำหลัก
ขอบคุณ

1
นี่คือข้อมูลโค้ดเพื่อชี้แจง[gist: unionBy.js ]
ขอบคุณ

@naomik หลังจากคิดใหม่วิธีการแก้ปัญหาของฉันในขณะที่ฉันไม่แน่ใจอีกต่อไปถ้ามันเป็นวิธีที่เหมาะสมที่จะผ่านภาคแสดง สิ่งที่คุณได้รับคือการเปลี่ยนแปลงองค์ประกอบของอาร์เรย์ที่สอง ฉันสงสัยว่าวิธีนี้แก้ได้มากกว่าแค่ปัญหาของเล่น

3

เพื่อประโยชน์ของมัน ... นี่คือทางออกบรรทัดเดียว:

const x = [...new Set([['C', 'B'],['B', 'A']].reduce( (a, e) => a.concat(e), []))].sort()
// ['A', 'B', 'C']

ไม่สามารถอ่านได้โดยเฉพาะ แต่อาจช่วยคนได้:

  1. ใช้ฟังก์ชันลดพร้อมกับค่าสะสมเริ่มต้นตั้งค่าเป็นอาร์เรย์ว่าง
  2. ฟังก์ชั่นลดใช้ concat เพื่อผนวกแต่ละแถวย่อยลงในอาร์เรย์ตัวสะสม
  3. Setผลจากการนี้จะถูกส่งเป็นพารามิเตอร์ตัวสร้างที่จะสร้างใหม่
  4. ผู้ประกอบการแพร่กระจายจะใช้ในการแปลงSetเป็นอาร์เรย์
  5. มีการsort()ใช้ฟังก์ชันกับอาร์เรย์ใหม่

2
นอกจากนี้reduce()คุณสามารถใช้แทนArray.from(set)
Eran Goldin

3

DeDuplicate เดี่ยวหรือผสานและ DeDuplicate หลายอินพุต ตัวอย่างด้านล่าง

ใช้ ES6 - ตั้งค่าสำหรับการทำลาย

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

ฟังก์ชั่นระยะสั้น (เพียง 9 บรรทัด)

/**
* This function merging only arrays unique values. It does not merges arrays in to array with duplicate values at any stage.
*
* @params ...args Function accept multiple array input (merges them to single array with no duplicates)
* it also can be used to filter duplicates in single array
*/
function arrayDeDuplicate(...args){
   let set = new Set(); // init Set object (available as of ES6)
   for(let arr of args){ // for of loops through values
      arr.map((value) => { // map adds each value to Set object
         set.add(value); // set.add method adds only unique values
      });
   }
   return [...set]; // destructuring set object back to array object
   // alternativly we culd use:  return Array.from(set);
}

ใช้ตัวอย่างโค้ด :

// SCENARIO 
let a = [1,2,3,4,5,6];
let b = [4,5,6,7,8,9,10,10,10];
let c = [43,23,1,2,3];
let d = ['a','b','c','d'];
let e = ['b','c','d','e'];

// USEAGE
let uniqueArrayAll = arrayDeDuplicate(a, b, c, d, e);
let uniqueArraySingle = arrayDeDuplicate(b);

// OUTPUT
console.log(uniqueArrayAll); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 43, 23, "a", "b", "c", "d", "e"]
console.log(uniqueArraySingle); // [4, 5, 6, 7, 8, 9, 10]

ทำไมต้องใช้arr.mapที่นี่? คุณกำลังใช้มันเป็นforeachผลลัพธ์จะถูกละเว้น
แอนโทนี

ฉันใช้return Array.from (set.values ​​()); เนื่องจาก vscode ให้ข้อผิดพลาดในการส่งคืน [... set];
makkasi


3

var array1 = ["one","two"];
var array2 = ["two", "three"];
var collectionOfTwoArrays = [...array1, ...array2];    
var uniqueList = array => [...new Set(array)];
console.log('Collection :');
console.log(collectionOfTwoArrays);    
console.log('Collection without duplicates :');
console.log(uniqueList(collectionOfTwoArrays));

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