Làm cách nào để có được sự khác biệt giữa hai mảng trong JavaScript?


753

Có cách nào để trả về sự khác biệt giữa hai mảng trong JavaScript không?

Ví dụ:

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

// need ["c", "d"]

9
Đối xứng hay không đối xứng?
Các cuộc đua nhẹ nhàng trong quỹ đạo

Với chức năng ES6 mới, điều này có thể được thực hiện như một lớp lót đơn giản (sẽ mất rất nhiều thời gian để có thể sử dụng trong tất cả các trình duyệt chính). Trong mọi trường hợp, hãy kiểm tra câu trả lời
Salvador Dali

1
một khía cạnh quan trọng của giải pháp là hiệu suất. độ phức tạp thời gian tiệm cận của loại hoạt động này - trong các ngôn ngữ khác - là O(a1.length x log(a2.length))- hiệu suất này có thể có trong JavaScript không?
Raul

Câu trả lời:


219

Tôi giả sử bạn đang so sánh một mảng bình thường. Nếu không, bạn cần thay đổi vòng lặp for thành vòng lặp for .. in .

function arr_diff (a1, a2) {

    var a = [], diff = [];

    for (var i = 0; i < a1.length; i++) {
        a[a1[i]] = true;
    }

    for (var i = 0; i < a2.length; i++) {
        if (a[a2[i]]) {
            delete a[a2[i]];
        } else {
            a[a2[i]] = true;
        }
    }

    for (var k in a) {
        diff.push(k);
    }

    return diff;
}

console.log(arr_diff(['a', 'b'], ['a', 'b', 'c', 'd']));
console.log(arr_diff("abcd", "abcde"));
console.log(arr_diff("zxc", "zxc"));

Một giải pháp tốt hơn, nếu bạn không quan tâm đến khả năng tương thích ngược, là sử dụng bộ lọc. Nhưng vẫn còn, giải pháp này hoạt động.


46
Điều này có thể hoạt động nhưng nó thực hiện ba vòng để hoàn thành những gì có thể được thực hiện trong một dòng mã bằng phương thức lọc của Array.
Joshaven Potter

9
Để rõ ràng, điều này thực hiện sự khác biệt đối xứng của a1a2 , không giống như các câu trả lời khác được đăng ở đây.
200_success

25
Đây không phải là câu trả lời tốt nhất, nhưng tôi cho nó một sự ủng hộ từ thiện, để giúp bù đắp cho những sự hạ bệ không công bằng. Chỉ những câu trả lời sai nên bị hạ cấp, và nếu tôi đang làm việc trong một dự án với các trình duyệt cruft trong phạm vi (thời điểm khó khăn xảy ra), câu trả lời này thậm chí có thể hữu ích.
Michael Scheper

3
Tôi có thể biết điều gì sẽ xảy ra khi var a1 = ['a', 'b'];var a2 = ['a', 'b', 'c', 'd', 'b'];, nó sẽ trở lại câu trả lời sai , tức là ['c', 'd', 'b']thay vì ['c', 'd'].
skbly7

4
Cách nhanh nhất là giải pháp rõ ràng ngây thơ nhất. Tôi đã thử nghiệm tất cả các giải pháp được đề xuất cho khác biệt đối xứng trong chuỗi này và người chiến thắng là:function diff2(a, b) { var i, la = a.length, lb = b.length, res = []; if (!la) return b; else if (!lb) return a; for (i = 0; i < la; i++) { if (b.indexOf(a[i]) === -1) res.push(a[i]); } for (i = 0; i < lb; i++) { if (a.indexOf(b[i]) === -1) res.push(b[i]); } return res; }
Nomaed

1233

Có một cách tốt hơn bằng ES7:


Ngã tư

 let intersection = arr1.filter(x => arr2.includes(x));

Giao lộ khác biệt Sơ đồ Venn

Đối với [1,2,3] [2,3]nó sẽ mang lại [2,3]. Mặt khác, vì [1,2,3] [2,3,5]sẽ trả lại điều tương tự.


Sự khác biệt

let difference = arr1.filter(x => !arr2.includes(x));

Biểu đồ Venn bên phải

Đối với [1,2,3] [2,3]nó sẽ mang lại [1]. Mặt khác, vì [1,2,3] [2,3,5]sẽ trả lại điều tương tự.


Đối với một sự khác biệt đối xứng , bạn có thể làm:

let difference = arr1
                 .filter(x => !arr2.includes(x))
                 .concat(arr2.filter(x => !arr1.includes(x)));

Biểu đồ Venn đối xứng

Bằng cách này, bạn sẽ nhận được một mảng chứa tất cả các phần tử của mảng1 không có trong mảng 2 và ngược lại

Như @Joshaven Potter đã chỉ ra câu trả lời của anh ấy, bạn có thể thêm nó vào Array.prototype để nó có thể được sử dụng như thế này:

Array.prototype.diff = function(arr2) { return this.filter(x => !arr2.includes(x)); }
[1, 2, 3].diff([2, 3])

3
Tôi thích kiểm tra < 0thay vì== -1
Vic

1
Tính toán sự Arraykhác biệt là một cái gọi là set operation, bởi vì tra cứu tài sản là công việc rất riêng của Sets, là các đơn đặt hàng có cường độ nhanh hơn sau đó indexOf/ includes. Nói một cách đơn giản, giải pháp của bạn rất kém hiệu quả và khá chậm.

@ftor nhưng với Set, giá trị phải là duy nhất, không?
Cổ tử cung

1
@LuisSieira Tôi hiểu rằng nó sẽ hoạt động với [1,2,3] [2,3,5]điều kiện là các con số là duy nhất nhưng nếu bạn đã nói [1,1,2,3] [1,2,3,5]và mong đợi thì [1]bạn không thể sử dụng Set. Tuy nhiên, giải pháp của bạn sẽ không hoạt động: - / Tôi đã kết thúc việc tạo chức năng này bởi vì tôi không thể tìm ra một cách thỏa đáng để thực hiện nó ngắn gọn hơn. Nếu bạn có bất kỳ ý tưởng nào về cách làm điều đó, tôi rất muốn biết!
Cổ tử cung

3
Không phải là Array.includes()tính năng ES7 thay vì ES6? (1) (2) - và để tiếp tục, với ES6 bạn có thể sử dụng , Array.some()ví dụ: let intersection = aArray.filter(a => bArray.some(b => a === b))không?
Jari Keinänen

910
Array.prototype.diff = function(a) {
    return this.filter(function(i) {return a.indexOf(i) < 0;});
};

////////////////////  
// Examples  
////////////////////

[1,2,3,4,5,6].diff( [3,4,5] );  
// => [1, 2, 6]

["test1", "test2","test3","test4","test5","test6"].diff(["test1","test2","test3","test4"]);  
// => ["test5", "test6"]

Lưu ý indexOf và bộ lọc không có sẵn trong tức là trước eg9.


50
Trình duyệt duy nhất không hỗ trợ bộ lọc và indexOf là IE8. IE9 không hỗ trợ cả hai. Vì vậy, nó không sai.
Bryan Larsen

14
eg7 và eg8 vẫn rất (không may) rất phù hợp, tuy nhiên bạn có thể tìm thấy mã polyfill cho cả hai chức năng trên trang MDN: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/ của nhà phát triển.mozilla.org/en/JavaScript/ Tham chiếu / Global_Objects / Tải trọng trong mã được liệt kê trong "khả năng tương thích" thông qua một điều kiện IE & BOOM. Ie7 / 8 được hỗ trợ.
1nfiniti

44
Giải pháp này có thời gian chạy là O (n ^ 2), một giải pháp tuyến tính sẽ hiệu quả hơn nhiều.
jholloman

75
Nếu bạn sử dụng chức năng như thế này: [1,2,3].diff([3,4,5])nó sẽ trả về [1,2]thay vì [1,2,4,5]vậy nó không giải quyết được vấn đề trong câu hỏi ban đầu, một điều cần lưu ý.
Bugster

12
@AlinPurcaru Không được hỗ trợ bởi các trình duyệt cổ! = Sai. Xem xét Netscape 2.0, hầu hết các mã JS ở đây là "sai" theo định nghĩa này. Đó là một điều ngớ ngẩn để nói.
NullUserException

304

Đây là cách dễ nhất để có được chính xác kết quả mà bạn đang tìm kiếm, sử dụng jQuery:

var diff = $(old_array).not(new_array).get();

diffbây giờ chứa những gì trong old_arrayđó không phải là trongnew_array


4
@Batman Có, nhưng chỉ khi chúng được tham chiếu đến cùng một đối tượng ( {a: 1} != {a: 1}) ( bằng chứng )
Matmarbon

Có thể sử dụng nó với các mảng chứa dữ liệu của một đối tượng tùy chỉnh không? Tôi đã thử nó thực sự theo cùng một cách nhưng nó không hoạt động. Bất kỳ ý tưởng sẽ được đánh giá cao.
LetMeCodeYou 30/1/2015

8
Đây có phải là một mẹo? Tài liệu coi phương thức này là một phần của Phương thức phần tử DOM chứ không phải là một trình trợ giúp mảng chung. Vì vậy, nó có thể hoạt động theo cách này ngay bây giờ, nhưng có thể không phải trong các phiên bản trong tương lai, vì nó không có ý định sử dụng nó theo cách này. Mặc dù, tôi rất vui nếu nó chính thức là một người trợ giúp mảng chung.
robsch

1
@robsch Khi bạn sử dụng .notvới một mảng, jQuery sử dụng tiện ích tích hợp sẵn .grep()dành riêng cho việc lọc các mảng. Tôi không thể thấy điều này thay đổi.
siêu âm

1
@vsync Âm thanh giống như bạn sau một sự khác biệt đối xứng
siêu âm

158

Phương pháp khác biệt trong Underscore (hoặc thay thế thả xuống của nó, Lo-Dash ) cũng có thể làm điều này:

(R)eturns the values from array that are not present in the other arrays

_.difference([1, 2, 3, 4, 5], [5, 2, 10]);
=> [1, 3, 4]

Như với bất kỳ chức năng Underscore nào, bạn cũng có thể sử dụng nó theo kiểu hướng đối tượng hơn:

_([1, 2, 3, 4, 5]).difference([5, 2, 10]);

4
Tôi nghĩ rằng đó là một giải pháp tốt về hiệu suất, đặc biệt là khi lodash và gạch dưới tiếp tục chiến đấu để thực hiện tốt nhất. Ngoài ra, nó tương thích với IE6.
mahemoff

4
Coi chừng, việc thực hiện này sẽ không hoạt động đối với các mảng của các đối tượng. Xem stackoverflow.com/q/8672383/14731 để biết thêm thông tin.
Gili

1
Là một trong những câu trả lời đề cập ở đó, nó hoạt động nếu nó là cùng một đối tượng, nhưng không phải nếu hai đối tượng có cùng thuộc tính. Tôi nghĩ điều đó không sao vì các khái niệm về sự bình đẳng khác nhau (ví dụ: nó cũng có thể là một thuộc tính "id" trong một số ứng dụng). Tuy nhiên, sẽ tốt nếu bạn có thể vượt qua bài kiểm tra so sánh để giao nhau ().
mahemoff

Đối với hậu thế: Giờ đây, Lodash có _.differenceBy () sẽ gọi lại để thực hiện so sánh; nếu bạn đang so sánh các đối tượng, bạn có thể thả vào một hàm so sánh chúng theo cách bạn cần.
Một sốCallMeTim

2
Hãy cẩn thận nếu thứ tự của các đối số đảo ngược, nó sẽ không hoạt động. ví dụ. _.difference ([5, 2, 10], [1, 2, 3, 4, 5]); không thể có được sự khác biệt
Russj

79

JavaScript đơn giản

Có hai điều không thể chấp nhận được đối với "sự khác biệt". Tôi sẽ cho phép bạn chọn cái nào bạn muốn. Nói rằng bạn có:

var a1 = ['a', 'b'     ];
var a2 = [     'b', 'c'];
  1. Nếu bạn muốn lấy ['a'], sử dụng chức năng này:

    function difference(a1, a2) {
      var result = [];
      for (var i = 0; i < a1.length; i++) {
        if (a2.indexOf(a1[i]) === -1) {
          result.push(a1[i]);
        }
      }
      return result;
    }
  2. Nếu bạn muốn lấy ['a', 'c'](tất cả các phần tử có trong một a1 hoặc a2, nhưng không phải cả hai - cái gọi là sự khác biệt đối xứng ), hãy sử dụng hàm này:

    function symmetricDifference(a1, a2) {
      var result = [];
      for (var i = 0; i < a1.length; i++) {
        if (a2.indexOf(a1[i]) === -1) {
          result.push(a1[i]);
        }
      }
      for (i = 0; i < a2.length; i++) {
        if (a1.indexOf(a2[i]) === -1) {
          result.push(a2[i]);
        }
      }
      return result;
    }

Tạm dừng / gạch dưới

Nếu bạn đang sử dụng lodash, bạn có thể sử dụng _.difference(a1, a2)(trường hợp 1 ở trên) hoặc_.xor(a1, a2) (trường hợp 2).

Nếu bạn đang sử dụng Underscore.js, bạn có thể sử dụng _.difference(a1, a2) hàm cho trường hợp 1.

ES6 Set, cho các mảng rất lớn

Các mã trên hoạt động trên tất cả các trình duyệt. Tuy nhiên, đối với các mảng lớn hơn khoảng 10.000 mặt hàng, nó trở nên khá chậm, vì nó có độ phức tạp O (n²). Trên nhiều trình duyệt hiện đại, chúng ta có thể tận dụng Setđối tượng ES6 để tăng tốc mọi thứ. Lodash tự động sử dụng Setkhi có sẵn. Nếu bạn không sử dụng lodash, hãy sử dụng cách triển khai sau, lấy cảm hứng từ bài đăng trên blog của Axel Rauschmayer :

function difference(a1, a2) {
  var a2Set = new Set(a2);
  return a1.filter(function(x) { return !a2Set.has(x); });
}

function symmetricDifference(a1, a2) {
  return difference(a1, a2).concat(difference(a2, a1));
}

Ghi chú

Hành vi cho tất cả các ví dụ có thể gây ngạc nhiên hoặc không rõ ràng nếu bạn quan tâm đến các mảng -0, +0, NaN hoặc thưa thớt. (Đối với hầu hết các mục đích sử dụng, điều này không thành vấn đề.)


Cảm tạ. bạn đã cứu ngày của tôi Tôi đã phải so sánh một mảng 300K và giải pháp "Set" của bạn hoạt động hoàn hảo. Đây phải là câu trả lời được chấp nhận.
justadev

52

Để có được khác biệt đối xứng, bạn cần so sánh các mảng theo cả hai cách (hoặc trong tất cả các cách trong trường hợp có nhiều mảng)

nhập mô tả hình ảnh ở đây


ES7 (Bản thảo năm 2016)

// diff between just two arrays:
function arrayDiff(a, b) {
    return [
        ...a.filter(x => !b.includes(x)),
        ...b.filter(x => !a.includes(x))
    ];
}

// diff between multiple arrays:
function arrayDiff(...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter(x => !unique.includes(x));
    }));
}

ES6 (Bản thảo năm 2015)

// diff between just two arrays:
function arrayDiff(a, b) {
    return [
        ...a.filter(x => b.indexOf(x) === -1),
        ...b.filter(x => a.indexOf(x) === -1)
    ];
}

// diff between multiple arrays:
function arrayDiff(...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter(x => unique.indexOf(x) === -1);
    }));
}

ES5 (Bản thảo 5.1)

// diff between just two arrays:
function arrayDiff(a, b) {
    var arrays = Array.prototype.slice.call(arguments);
    var diff = [];

    arrays.forEach(function(arr, i) {
        var other = i === 1 ? a : b;
        arr.forEach(function(x) {
            if (other.indexOf(x) === -1) {
                diff.push(x);
            }
        });
    })

    return diff;
}

// diff between multiple arrays:
function arrayDiff() {
    var arrays = Array.prototype.slice.call(arguments);
    var diff = [];

    arrays.forEach(function(arr, i) {
        var others = arrays.slice(0);
        others.splice(i, 1);
        var otherValues = Array.prototype.concat.apply([], others);
        var unique = otherValues.filter(function (x, j) { 
            return otherValues.indexOf(x) === j; 
        });
        diff = diff.concat(arr.filter(x => unique.indexOf(x) === -1));
    });
    return diff;
}

Thí dụ:

// diff between two arrays:
const a = ['a', 'd', 'e'];
const b = ['a', 'b', 'c', 'd'];
arrayDiff(a, b); // (3) ["e", "b", "c"]

// diff between multiple arrays
const a = ['b', 'c', 'd', 'e', 'g'];
const b = ['a', 'b'];
const c = ['a', 'e', 'f'];
arrayDiff(a, b, c); // (4) ["c", "d", "g", "f"]

Sự khác biệt giữa các mảng của đối tượng

function arrayDiffByKey(key, ...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter( x =>
            !unique.some(y => x[key] === y[key])
        );
    }));
}

Thí dụ:

const a = [{k:1}, {k:2}, {k:3}];
const b = [{k:1}, {k:4}, {k:5}, {k:6}];
const c = [{k:3}, {k:5}, {k:7}];
arrayDiffByKey('k', a, b, c); // (4) [{k:2}, {k:4}, {k:6}, {k:7}]

50

Một cách tiếp cận sạch hơn trong ES6 là giải pháp sau đây.

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

Sự khác biệt

a2.filter(d => !a1.includes(d)) // gives ["c", "d"]

Ngã tư

a2.filter(d => a1.includes(d)) // gives ["a", "b"]

Liên minh rời rạc (Sự khác biệt đối xứng)

[ ...a2.filter(d => !a1.includes(d)),
  ...a1.filter(d => !a2.includes(d)) ]

Nó chỉ hoạt động theo một hướng. Bây giờ hãy tưởng tượng rằng a1 = ['a', 'b', 'e']: e sẽ không được trích xuất.
imrok

vâng, đó là cách mà sự khác biệt trong lý thuyết tập hợp hoạt động. (a2 -a1) những gì bạn đang tìm kiếm là (a2-a1) + (a1-a2)
ifelse.codes

1
@imrok Tôi tin rằng đây là những gì bạn đang tìm kiếm [... a2.filter (d =>! a1.includes (d)), ... (a1.filter (d =>! a2.includes (d)) )]
ifelse.codes

2
Giải pháp đẹp, cảm ơn!
Thiago Alves

40

Bạn có thể sử dụng Set trong trường hợp này. Nó được tối ưu hóa cho loại hoạt động này (liên minh, giao lộ, sự khác biệt).

Hãy chắc chắn rằng nó áp dụng cho trường hợp của bạn, một khi nó cho phép không trùng lặp.

var a = new JS.Set([1,2,3,4,5,6,7,8,9]);
var b = new JS.Set([2,4,6,8]);

a.difference(b)
// -> Set{1,3,5,7,9}

4
Trông giống như một thư viện đẹp! Thật xấu hổ khi bạn không thể tải xuống chỉ Setchức năng mà không phải lấy mọi thứ khác ...
Blixt

@Blixt Tôi tin rằng bạn có thể tải xuống tất cả và chỉ bao gồm tệp set.js
Samuel Carrijo

Đặt được thực hiện trong google đóng cửa là tốt. clos
Ben Flynn


32
function diff(a1, a2) {
  return a1.concat(a2).filter(function(val, index, arr){
    return arr.indexOf(val) === arr.lastIndexOf(val);
  });
}

Hợp nhất cả hai mảng, các giá trị duy nhất sẽ chỉ xuất hiện một lần, vì vậy indexOf () sẽ giống như lastIndexOf ().


3
Tôi đồng ý rằng đây là cách sạch nhất và đơn giản nhất và tốt đẹp mà nó không yêu cầu phải chạm vào nguyên mẫu. Nếu bạn không thể giải thích điều đó với một đứa trẻ sáu tuổi, bạn sẽ không tự hiểu điều đó. - Albert Einstein
lacostenycoder

18

để trừ một mảng từ mảng khác, chỉ cần sử dụng đoạn mã dưới đây:

var a1 = ['1','2','3','4','6'];
var a2 = ['3','4','5'];

var items = new Array();

items = jQuery.grep(a1,function (item) {
    return jQuery.inArray(item, a2) < 0;
});

Nó sẽ trả về ['1,' 2 ',' 6 '] là các mục của mảng thứ nhất không tồn tại trong phần thứ hai.

Do đó, theo mẫu vấn đề của bạn, mã sau đây là giải pháp chính xác:

var array1 = ["test1", "test2","test3", "test4"];
var array2 = ["test1", "test2","test3","test4", "test5", "test6"];

var _array = new Array();

_array = jQuery.grep(array2, function (item) {
     return jQuery.inArray(item, array1) < 0;
});

14

Với sự xuất hiện của ES6 với các bộ và toán tử splat (tại thời điểm chỉ hoạt động trong Firefox, hãy kiểm tra bảng tương thích ), bạn có thể viết một lớp lót sau:

var a = ['a', 'b', 'c', 'd'];
var b = ['a', 'b'];
var b1 = new Set(b);
var difference = [...new Set([...a].filter(x => !b1.has(x)))];

Điều đó sẽ dẫn đến [ "c", "d" ].


Chỉ tò mò làm thế nào mà khác hơn là làmb.filter(x => !a.indexOf(x)))
chovy

1
@chovy nó khác thời gian phức tạp. Giải pháp của tôi là giải pháp O(n + m)của bạn là O(n * m)trong đó n và m là độ dài của mảng. Lấy danh sách dài và giải pháp của tôi sẽ chạy trong vài giây, trong khi của bạn sẽ mất hàng giờ.
Salvador Dali

Còn việc so sánh một thuộc tính của danh sách các đối tượng thì sao? Có thể sử dụng giải pháp này?
chovy

2
a.filter(x => !b1.has(x))đơn giản hơn Và lưu ý spec chỉ đòi hỏi sự phức tạp là n * f(m) + mvới f(m)sublinear trên trung bình. Nó tốt hơn n * m, nhưng không nhất thiết n + m.
Oriol

2
@SalvadorDali var difference = [...new Set([...a].filter(x => !b1.has(x)))];Tại sao bạn tạo bản sao 'a'? Tại sao bạn biến kết quả của bộ lọc thành một tập hợp và sau đó trở lại thành mảng? Điều này không tương đương vớivar difference = a.filter(x => !b1.has(x));
Deepak Mittal

13

Phương pháp tiếp cận chức năng với ES2015

Tính toán differencegiữa hai mảng là một trong các Sethoạt động. Thuật ngữ đã chỉ ra rằng Setloại bản địa nên được sử dụng, để tăng tốc độ tra cứu. Dù sao, có ba hoán vị khi bạn tính toán sự khác biệt giữa hai bộ:

[+left difference] [-intersection] [-right difference]
[-left difference] [-intersection] [+right difference]
[+left difference] [-intersection] [+right difference]

Đây là một giải pháp chức năng phản ánh những hoán vị này.

Trái difference:

// small, reusable auxiliary functions

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


// left difference

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


// mock data

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


// run the computation

console.log( differencel(xs) (ys) );

Phải difference:

differencerlà tầm thường. Nó chỉ là differencelvới các đối số lật. Bạn có thể viết một hàm cho thuận tiện:const differencer = flip(differencel) . Đó là tất cả!

Đối xứng difference:

Bây giờ chúng ta có bên trái và bên phải, việc thực hiện đối xứng cũng differencetrở nên tầm thường:

// small, reusable auxiliary functions

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


// left difference

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


// symmetric difference

const difference = ys => xs =>
 concat(differencel(xs) (ys)) (flip(differencel) (xs) (ys));

// mock data

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


// run the computation

console.log( difference(xs) (ys) );

Tôi đoán ví dụ này là một điểm khởi đầu tốt để có được ấn tượng về ý nghĩa của lập trình chức năng:

Lập trình với các khối xây dựng có thể được cắm với nhau theo nhiều cách khác nhau.


12

Một giải pháp sử dụng indexOf()sẽ ổn đối với các mảng nhỏ nhưng khi chúng phát triển theo chiều dài thì hiệu suất của các phương pháp tiếp cận thuật toán O(n^2). Đây là một giải pháp sẽ hoạt động tốt hơn cho các mảng rất lớn bằng cách sử dụng các đối tượng làm mảng kết hợp để lưu trữ các mục mảng dưới dạng các khóa; nó cũng tự động loại bỏ các mục trùng lặp nhưng chỉ hoạt động với các giá trị chuỗi (hoặc các giá trị có thể được lưu trữ an toàn dưới dạng chuỗi):

function arrayDiff(a1, a2) {
  var o1={}, o2={}, diff=[], i, len, k;
  for (i=0, len=a1.length; i<len; i++) { o1[a1[i]] = true; }
  for (i=0, len=a2.length; i<len; i++) { o2[a2[i]] = true; }
  for (k in o1) { if (!(k in o2)) { diff.push(k); } }
  for (k in o2) { if (!(k in o1)) { diff.push(k); } }
  return diff;
}

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
arrayDiff(a1, a2); // => ['c', 'd']
arrayDiff(a2, a1); // => ['c', 'd']

Bạn muốn sử dụng Object.hasOwnProperty () bất cứ khi nào bạn thực hiện "cho vào" trên một đối tượng. Nếu không, bạn có nguy cơ lặp qua mọi trường được thêm vào nguyên mẫu của Đối tượng mặc định. (Hoặc chỉ là cha mẹ của đối tượng của bạn) Ngoài ra, bạn chỉ cần hai vòng lặp, một vòng để tạo bảng băm và cái còn lại tìm trên bảng băm đó.
jholloman

1
@jholloman Tôi tôn trọng không đồng ý . Bây giờ chúng tôi có thể kiểm soát số lượng cho bất kỳ tài sản nào, có lẽ bạn nên bao gồm bất kỳ tài sản nào bạn nhận được trong quá trình liệt kê.
Phrogz

1
@Phrogz Một điểm tốt nếu bạn chỉ lo lắng về các trình duyệt hiện đại. Thật không may, tôi phải hỗ trợ trở lại IE7 tại nơi làm việc vì vậy thời kỳ đồ đá là lối suy nghĩ mặc định của tôi và chúng tôi không có xu hướng sử dụng miếng chêm.
jholloman

10

Câu trả lời trên của Joshaven Potter là tuyệt vời. Nhưng nó trả về các phần tử trong mảng B không nằm trong mảng C, nhưng không phải là cách khác. Ví dụ: nếu var a=[1,2,3,4,5,6].diff( [3,4,5,7]);sau đó nó sẽ xuất ra: ==> [1,2,6], nhưng không [1,2,6,7] , đó là sự khác biệt thực tế giữa hai. Bạn vẫn có thể sử dụng mã của Potter ở trên nhưng chỉ cần làm lại so sánh một lần nữa:

Array.prototype.diff = function(a) {
    return this.filter(function(i) {return !(a.indexOf(i) > -1);});
};

////////////////////  
// Examples  
////////////////////

var a=[1,2,3,4,5,6].diff( [3,4,5,7]);
var b=[3,4,5,7].diff([1,2,3,4,5,6]);
var c=a.concat(b);
console.log(c);

Điều này sẽ xuất ra: [ 1, 2, 6, 7 ]


9

Một cách khác để giải quyết vấn đề

function diffArray(arr1, arr2) {
    return arr1.concat(arr2).filter(function (val) {
        if (!(arr1.includes(val) && arr2.includes(val)))
            return val;
    });
}

diffArray([1, 2, 3, 7], [3, 2, 1, 4, 5]);    // return [7, 4, 5]

Ngoài ra, bạn có thể sử dụng cú pháp hàm mũi tên:

const diffArray = (arr1, arr2) => arr1.concat(arr2)
    .filter(val => !(arr1.includes(val) && arr2.includes(val)));

diffArray([1, 2, 3, 7], [3, 2, 1, 4, 5]);    // return [7, 4, 5]

7
Array.prototype.difference = function(e) {
    return this.filter(function(i) {return e.indexOf(i) < 0;});
};

eg:- 

[1,2,3,4,5,6,7].difference( [3,4,5] );  
 => [1, 2, 6 , 7]

Bạn không bao giờ nên mở rộng một đối tượng bản địa theo cách này. Nếu tiêu chuẩn giới thiệu differencelà hàm trong phiên bản tương lai và chức năng này có chữ ký hàm khác thì bạn sẽ phá vỡ mã của bạn hoặc các thư viện nước ngoài sử dụng chức năng này.
t.niese

7

Giải pháp rất đơn giản với chức năng lọc của JavaScript:

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

function diffArray(arr1, arr2) {
  var newArr = [];
  var myArr = arr1.concat(arr2);
  
    newArr = myArr.filter(function(item){
      return arr2.indexOf(item) < 0 || arr1.indexOf(item) < 0;
    });
   alert(newArr);
}

diffArray(a1, a2);


6

Còn cái này thì sao:

Array.prototype.contains = function(needle){
  for (var i=0; i<this.length; i++)
    if (this[i] == needle) return true;

  return false;
} 

Array.prototype.diff = function(compare) {
    return this.filter(function(elem) {return !compare.contains(elem);})
}

var a = new Array(1,4,7, 9);
var b = new Array(4, 8, 7);
alert(a.diff(b));

Vì vậy, theo cách này bạn có thể làm array1.diff(array2)để có được sự khác biệt của chúng (Độ phức tạp thời gian khủng khiếp cho thuật toán mặc dù - O (mảng1.length x mảng2.length) Tôi tin)


Sử dụng tùy chọn bộ lọc là một ý tưởng tuyệt vời ... tuy nhiên, bạn không cần tạo phương thức chứa cho Mảng. Tôi đã chuyển đổi ý tưởng của bạn thành một lớp lót ... Cảm ơn đã truyền cảm hứng!
Joshaven Potter

Bạn không cần xác định hàm chứa (). JS bao gồm () làm điều tương tự.
Da Man

4

Sử dụng http://phrogz.net/JS/ArraySetMath.js bạn có thể:

var array1 = ["test1", "test2","test3", "test4"];
var array2 = ["test1", "test2","test3","test4", "test5", "test6"];

var array3 = array2.subtract( array1 );
// ["test5", "test6"]

var array4 = array1.exclusion( array2 );
// ["test5", "test6"]

4
function diffArray(arr1, arr2) {
  var newArr = arr1.concat(arr2);
  return newArr.filter(function(i){
    return newArr.indexOf(i) == newArr.lastIndexOf(i);
  });
}

đây là công việc cho tôi


3
  • Giải pháp JavaScript thuần túy (không có thư viện)
  • Tương thích với các trình duyệt cũ hơn (không sử dụng filter)
  • Ô (n ^ 2)
  • fnTham số gọi lại tùy chọn cho phép bạn chỉ định cách so sánh các mục mảng

function diff(a, b, fn){
    var max = Math.max(a.length, b.length);
        d = [];
    fn = typeof fn === 'function' ? fn : false
    for(var i=0; i < max; i++){
        var ac = i < a.length ? a[i] : undefined
            bc = i < b.length ? b[i] : undefined;
        for(var k=0; k < max; k++){
            ac = ac === undefined || (k < b.length && (fn ? fn(ac, b[k]) : ac == b[k])) ? undefined : ac;
            bc = bc === undefined || (k < a.length && (fn ? fn(bc, a[k]) : bc == a[k])) ? undefined : bc;
            if(ac == undefined && bc == undefined) break;
        }
        ac !== undefined && d.push(ac);
        bc !== undefined && d.push(bc);
    }
    return d;
}

alert(
    "Test 1: " + 
    diff(
        [1, 2, 3, 4],
        [1, 4, 5, 6, 7]
      ).join(', ') +
    "\nTest 2: " +
    diff(
        [{id:'a',toString:function(){return this.id}},{id:'b',toString:function(){return this.id}},{id:'c',toString:function(){return this.id}},{id:'d',toString:function(){return this.id}}],
        [{id:'a',toString:function(){return this.id}},{id:'e',toString:function(){return this.id}},{id:'f',toString:function(){return this.id}},{id:'d',toString:function(){return this.id}}],
        function(a, b){ return a.id == b.id; }
    ).join(', ')
);


Bạn có thể lưu trữ các giá trị độ dài để tăng thêm tốc độ. Tôi muốn khuyên bạn nên truy cập các phần tử mảng mà không kiểm tra độ dài, nhưng rõ ràng việc kiểm tra đơn giản mang lại tốc độ tăng tốc gần gấp 100 lần.
Slotos

Không có lý do để lưu trữ lengthgiá trị. Đó là tài sản đơn giản. jsperf.com/array-length-caching
vp_arth

3

Điều này đang hoạt động: về cơ bản hợp nhất hai mảng, tìm kiếm các bản sao và đẩy những gì không trùng lặp thành một mảng mới, đó là sự khác biệt.

function diff(arr1, arr2) {
  var newArr = [];
  var arr = arr1.concat(arr2);
  
  for (var i in arr){
    var f = arr[i];
    var t = 0;
    for (j=0; j<arr.length; j++){
      if(arr[j] === f){
        t++; 
        }
    }
    if (t === 1){
      newArr.push(f);
        }
  } 
  return newArr;
}


3

// tiếp cận es6

function diff(a, b) {
  var u = a.slice(); //dup the array
  b.map(e => {
    if (u.indexOf(e) > -1) delete u[u.indexOf(e)]
    else u.push(e)   //add non existing item to temp array
  })
  return u.filter((x) => {return (x != null)}) //flatten result
}

3

Độ phức tạp đối xứngtuyến tính . Yêu cầu ES6.

function arrDiff(arr1, arr2) {
    var arrays = [arr1, arr2].sort((a, b) => a.length - b.length);
    var smallSet = new Set(arrays[0]);

    return arrays[1].filter(x => !smallSet.has(x));
}


2

Chỉ cần suy nghĩ ... vì lợi ích của một thách thức ;-) điều này sẽ hoạt động ... (đối với các mảng cơ bản của chuỗi, số, v.v.) không có mảng lồng nhau

function diffArrays(arr1, arr2, returnUnion){
  var ret = [];
  var test = {};
  var bigArray, smallArray, key;
  if(arr1.length >= arr2.length){
    bigArray = arr1;
    smallArray = arr2;
  } else {
    bigArray = arr2;
    smallArray = arr1;
  }
  for(var i=0;i<bigArray.length;i++){
    key = bigArray[i];
    test[key] = true;
  }
  if(!returnUnion){
    //diffing
    for(var i=0;i<smallArray.length;i++){
      key = smallArray[i];
      if(!test[key]){
        test[key] = null;
      }
    }
  } else {
    //union
    for(var i=0;i<smallArray.length;i++){
      key = smallArray[i];
      if(!test[key]){
        test[key] = true;
      }
    }
  }
  for(var i in test){
    ret.push(i);
  }
  return ret;
}

array1 = "test1", "test2","test3", "test4", "test7"
array2 = "test1", "test2","test3","test4", "test5", "test6"
diffArray = diffArrays(array1, array2);
//returns ["test5","test6","test7"]

diffArray = diffArrays(array1, array2, true);
//returns ["test1", "test2","test3","test4", "test5", "test6","test7"]

Lưu ý việc sắp xếp có thể sẽ không được như đã lưu ý ở trên ... nhưng nếu muốn, hãy gọi .sort () trên mảng để sắp xếp nó.


2

Tôi muốn một hàm tương tự có một mảng cũ và một mảng mới và đưa cho tôi một mảng các mục được thêm vào và một mảng các mục bị loại bỏ, và tôi muốn nó có hiệu quả (vì vậy không có .contains!).

Bạn có thể chơi với giải pháp đề xuất của tôi ở đây: http://jsbin.com/osewu3/12 .

Bất cứ ai cũng có thể thấy bất kỳ vấn đề / cải tiến cho thuật toán đó? Cảm ơn!

Mã danh sách:

function diff(o, n) {
  // deal with empty lists
  if (o == undefined) o = [];
  if (n == undefined) n = [];

  // sort both arrays (or this won't work)
  o.sort(); n.sort();

  // don't compare if either list is empty
  if (o.length == 0 || n.length == 0) return {added: n, removed: o};

  // declare temporary variables
  var op = 0; var np = 0;
  var a = []; var r = [];

  // compare arrays and add to add or remove lists
  while (op < o.length && np < n.length) {
      if (o[op] < n[np]) {
          // push to diff?
          r.push(o[op]);
          op++;
      }
      else if (o[op] > n[np]) {
          // push to diff?
          a.push(n[np]);
          np++;
      }
      else {
          op++;np++;
      }
  }

  // add remaining items
  if( np < n.length )
    a = a.concat(n.slice(np, n.length));
  if( op < o.length )
    r = r.concat(o.slice(op, o.length));

  return {added: a, removed: r}; 
}

2

Tôi đang tìm kiếm một câu trả lời đơn giản không liên quan đến việc sử dụng các thư viện khác nhau và tôi đã đưa ra câu trả lời của riêng mình mà tôi không nghĩ đã được đề cập ở đây. Tôi không biết nó hiệu quả như thế nào hoặc bất cứ điều gì nhưng nó hoạt động;

    function find_diff(arr1, arr2) {
      diff = [];
      joined = arr1.concat(arr2);
      for( i = 0; i <= joined.length; i++ ) {
        current = joined[i];
        if( joined.indexOf(current) == joined.lastIndexOf(current) ) {
          diff.push(current);
        }
      }
      return diff;
    }

Đối với mã của tôi, tôi cũng cần các bản sao được lấy ra, nhưng tôi đoán rằng không phải lúc nào cũng được ưa thích.

Tôi đoán nhược điểm chính là nó có khả năng so sánh nhiều lựa chọn đã bị từ chối.


2

sửa chữa bitcoin cho câu trả lời tốt nhất

function arr_diff(a1, a2)
{
  var a=[], diff=[];
  for(var i=0;i<a1.length;i++)
    a[a1[i]]=a1[i];
  for(var i=0;i<a2.length;i++)
    if(a[a2[i]]) delete a[a2[i]];
    else a[a2[i]]=a2[i];
  for(var k in a)
   diff.push(a[k]);
  return diff;
}

điều này sẽ xem xét loại yếu tố hiện tại. b / c khi chúng tôi tạo [a1 [i]], nó chuyển đổi một giá trị thành chuỗi từ giá trị gốc của nó, vì vậy chúng tôi đã mất giá trị thực tế.


Điều này vẫn thất bại cho một loạt các đối tượng. var a = [{a: 1}], b = [{b: 2}] Array_diff (a, b) == [].
EricP
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.