Mã đơn giản nhất cho giao điểm mảng trong javascript


609

Mã đơn giản nhất, không có thư viện để thực hiện các giao điểm mảng trong javascript là gì? tôi muốn viết

intersection([1,2,3], [2,3,4,5])

và lấy

[2, 3]

16
Bạn muốn đơn giản hay nhanh chóng?
SLaks

11
Ưu tiên rất đơn giản, nhưng nó không thể chết não đến mức nó sẽ là một con lợn biểu diễn :)
Peter

4
Tôi đã tạo một trang thử nghiệm JsFiddle Banchmark cho tất cả các phương thức ở đây, bao gồm cảhàm giao nhau _underscore . (cao hơn là tốt hơn) ! nhập mô tả hình ảnh ở đây Cho đến bây giờ giao lộ_safe đã cho kết quả tốt nhất . BẠN & gạch dưới những điều tồi tệ nhất.
neoswf

Thêm a breakđể Simple js loopstăng ops / giây lên ~ 10M
Richard

19
Trong trường hợp bạn bỏ lỡ nó : câu trả lời đơn giản nhất không phải là câu trả lời được chấp nhận mà là câu trả lời ở dưới cùng: stackoverflow.com/questions/1885557/
Kẻ

Câu trả lời:


1080

Sử dụng kết hợp Array.prototype.filterArray.prototype.indexOf:

array1.filter(value => -1 !== array2.indexOf(value))

Hoặc như vrugtehagel đã đề xuất trong các nhận xét, bạn có thể sử dụng mã gần đây hơn Array.prototype.includescho mã đơn giản hơn:

array1.filter(value => array2.includes(value))

Đối với các trình duyệt cũ hơn:

array1.filter(function(n) {
    return array2.indexOf(n) !== -1;
});

9
Mà bạn có thể giải quyết bằng cách thêm phiên bản thư viện trên nguyên mẫu của mảng.
Anon.

12
Vâng, nhưng nó đáng được đề cập.
Tim Xuống

18
Câu trả lời hay nhất ở đây, cả vì đơn giản và làm việc với những người không phải là số
Muhd

41
intersection([1,2,1,1,3], [1])trả lại [1, 1, 1]. Nó không nên trở lại chỉ [1]?
edjroot

21
Thay vì array2.indexOf(n) != -1người ta cũng có thể viết array2.includes(n)cho mã thậm chí đơn giản hơn.
vrugtehagel

157

Phá hủy có vẻ đơn giản nhất, đặc biệt nếu chúng ta có thể giả sử đầu vào được sắp xếp:

/* destructively finds the intersection of 
 * two arrays in a simple fashion.  
 *
 * PARAMS
 *  a - first array, must already be sorted
 *  b - second array, must already be sorted
 *
 * NOTES
 *  State of input arrays is undefined when
 *  the function returns.  They should be 
 *  (prolly) be dumped.
 *
 *  Should have O(n) operations, where n is 
 *    n = MIN(a.length, b.length)
 */
function intersection_destructive(a, b)
{
  var result = [];
  while( a.length > 0 && b.length > 0 )
  {  
     if      (a[0] < b[0] ){ a.shift(); }
     else if (a[0] > b[0] ){ b.shift(); }
     else /* they're equal */
     {
       result.push(a.shift());
       b.shift();
     }
  }

  return result;
}

Không phá hủy phải phức tạp hơn, vì chúng tôi phải theo dõi các chỉ số:

/* finds the intersection of 
 * two arrays in a simple fashion.  
 *
 * PARAMS
 *  a - first array, must already be sorted
 *  b - second array, must already be sorted
 *
 * NOTES
 *
 *  Should have O(n) operations, where n is 
 *    n = MIN(a.length(), b.length())
 */
function intersect_safe(a, b)
{
  var ai=0, bi=0;
  var result = [];

  while( ai < a.length && bi < b.length )
  {
     if      (a[ai] < b[bi] ){ ai++; }
     else if (a[ai] > b[bi] ){ bi++; }
     else /* they're equal */
     {
       result.push(a[ai]);
       ai++;
       bi++;
     }
  }

  return result;
}

14
Có rất nhiều lỗi trong intersect_safe: lengthlà một thuộc tính trong Mảng, không phải là một phương thức. Có một biến không mong muốn itrong result.push(a[i]);. Cuối cùng, điều này chỉ đơn giản là không hoạt động trong trường hợp chung: hai đối tượng không lớn hơn đối tượng khác theo >toán tử không nhất thiết phải bằng nhau. intersect_safe( [ {} ], [ {} ] ), ví dụ, sẽ đưa ra (một khi các lỗi đã đề cập trước đó được sửa) một mảng có một phần tử, điều này rõ ràng là sai.
Tim Down

1
@Tim Down: Đã sửa các lỗi cú pháp mà bạn đã chỉ ra. Cho dù đó là chính xác hay không chính xác để xem xét bất cứ điều gì không phải là gerater hay ít hơn là bằng nhau tùy thuộc vào các yêu cầu. Tôi không nhận thấy bất cứ điều gì trong câu hỏi ban đầu nói rằng nội dung dự kiến ​​sẽ chứa mảng. Bây giờ, bạn có quyền nói rằng nên xử lý đầu vào bất ngờ, nhưng nếu thông số kỹ thuật đã xảy ra để ra lệnh đầu vào đó phải là mảng các số (như tôi giả định) thì mã sẽ ổn.
atk

1
@atk: Tôi lấy quan điểm của bạn, xem như ví dụ trong câu hỏi sử dụng các mảng chỉ chứa số.
Tim Xuống

4
Bạn cũng có thể sử dụng .slice(0)để tạo một bản sao của mảng intersect_safe, thay vì theo dõi các chỉ mục.
johnluetke

1
@thesmart: bạn nói đúng, chắc chắn có nhiều cách hiệu quả hơn để làm điều đó. Mã, ở trên, được dự định là đơn giản, không nhanh chóng :)
atk

59

Nếu môi trường của bạn hỗ trợ ECMAScript 6 Set , một cách đơn giản và được cho là hiệu quả (xem liên kết đặc tả):

function intersect(a, b) {
  var setA = new Set(a);
  var setB = new Set(b);
  var intersection = new Set([...setA].filter(x => setB.has(x)));
  return Array.from(intersection);
}

Ngắn hơn, nhưng ít đọc hơn (cũng không tạo giao lộ bổ sung Set):

function intersect(a, b) {
      return [...new Set(a)].filter(x => new Set(b).has(x));
}

Tránh một cái mới Set từ bmọi lúc:

function intersect(a, b) {
      var setB = new Set(b);
      return [...new Set(a)].filter(x => setB.has(x));
}

Lưu ý rằng khi sử dụng các bộ bạn sẽ chỉ nhận được các giá trị riêng biệt, do đó sẽ new Set[1,2,3,3].sizeước tính 3.


1
[...setA]cú pháp này là gì? Một số loại hoạt động javascript đặc biệt?
jxramos

1
@jxramos là cú pháp lây lan và trong trường hợp này nó chỉ được sử dụng để tạo một mảng từ các phần tử trong tập hợp. "Array.from (setA)" cũng sẽ hoạt động trong trường hợp này, nhưng vì câu hỏi yêu cầu "đơn giản nhất", tôi đã cố gắng làm cho nó sạch hơn để đọc trên dòng đó. Về vấn đề đó, tôi nghĩ mã có thể đơn giản hơn, vì vậy tôi sẽ cập nhật đoạn mã.
nbarbosa

@nbarbosa Tôi tò mò: tại sao bạn lại "nhân bản" mảng a? #filter không phá hủy mảng ban đầu, phải không? Nó tạo ra một mảng mới?
bplittle

@bplittle Tôi chỉ tạo một mảng từ tập hợp để loại bỏ trùng lặp, nếu không, sử dụng mảng trực tiếp sẽ dẫn đến trả về trùng lặp. Ví dụ: nếu tôi sử dụng mảng trực tiếp, giao nhau ([1,2,2,4], [2,3]) sẽ mang lại [2, 2].
nbarbosa

2
Không phải x => new Set(b).has(x)chức năng mũi tên đó biến bthành một tập hợp mỗi khi nó được thực thi sao? Bạn có thể nên lưu tập hợp đó trong một biến.
Aran-Fey

39

Sử dụng Underscore.js hoặc lodash.js

_.intersection( [0,345,324] , [1,0,324] )  // gives [0,324]

20
Op yêu cầu "thư viện miễn phí".
LinuxDiscipl

@LinuxDiscipl Lỗi của tôi vì đã bỏ lỡ điều đó. Cảm ơn bạn đã ghi chú
Sai Ram

33
Trong mọi trường hợp, đây là danh sách google hàng đầu cho tìm kiếm này vì vậy có câu trả lời thư viện là hữu ích. Cảm ơn.
webnoob

Tôi cũng rất vui vì điều này đã được đăng. Lần đầu tiên tôi cảm thấy cần phải có dấu gạch dưới. Thông thường bản đồ JavaScript và giảm các đường ống thực hiện công việc một cách thanh lịch nhưng không phải lúc này.
Sridhar Sarnobat

Tôi <3 gạch dưới và tôi <3 Jeremy Ashkenas (người tạo ra nó), nhưng ngay cả như vậy tôi rất khuyến khích kiểm tra Lodash thay thế. Về cơ bản, đây là phiên bản ưu việt của Underscore (ban đầu là một ngã ba) với nhược điểm duy nhất là mã nguồn siêu tối ưu hóa (và do đó gần như không thể đọc được). Những người Underscore thậm chí đã cân nhắc loại bỏ Underscore hoàn toàn (và chỉ bảo mọi người sử dụng Lodash), nhưng những người quan tâm đến khả năng đọc mã nguồn đã lập luận để giữ nó (thực ra tôi đã ở bên đó, nhưng tôi đã chuyển đổi thành Lodash). @see github.com/jashkenas/underscore/issues/2182
máy tính

14

Đóng góp của tôi trong các điều khoản ES6. Nói chung, nó tìm thấy giao điểm của một mảng với số lượng mảng không xác định được cung cấp làm đối số.

Array.prototype.intersect = function(...a) {
  return [this,...a].reduce((p,c) => p.filter(e => c.includes(e)));
}
var arrs = [[0,2,4,6,8],[4,5,6,7],[4,6]],
     arr = [0,1,2,3,4,5,6,7,8,9];

document.write("<pre>" + JSON.stringify(arr.intersect(...arrs)) + "</pre>");


mã này trông tuyệt vời, nhưng tôi không hiểu nó hoàn toàn. Có thể giải thích nó xin vui lòng?
bất thường

1
@novembersky Nó tập hợp tất cả các mảng chủ đề trong một mảng như thế [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7],[4,6]]và sau đó áp dụng .reduce(). [0,1,2,3,4,5,6,7,8,9].filter( e => [0,2,4,6,8].includes(e)Hoạt động đầu tiên được thực hiện và kết quả trở thành mới pctrở thành [4,5,6,7]trong lượt tiếp theo và tiếp tục như vậy cho đến khi không còn nữa c.
Redu

1
Đây là một giải pháp đắt tiền nếu bạn đang làm việc với các tập dữ liệu lớn.
Madbreaks

1
Đừng sửa đổi những thứ prototypekhông cần thiết.
fregante

14

// Return elements of array a that are also in b in linear time:
function intersect(a, b) {
  return a.filter(Set.prototype.has, new Set(b));
}

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

Tôi đề xuất giải pháp ngắn gọn ở trên, vượt trội so với các triển khai khác trên các đầu vào lớn. Nếu hiệu suất trên các đầu vào nhỏ có vấn đề, hãy kiểm tra các lựa chọn thay thế bên dưới.

Các lựa chọn thay thế và so sánh hiệu suất:

Xem đoạn mã sau để biết cách triển khai thay thế và kiểm tra https://jsperf.com/array-intersection-comparison để so sánh hiệu suất.

Kết quả trong Firefox 53:

  • Ops / giây trên mảng lớn (10.000 phần tử):

    filter + has (this)               523 (this answer)
    for + has                         482
    for-loop + in                     279
    filter + in                       242
    for-loops                          24
    filter + includes                  14
    filter + indexOf                   10
  • Ops / giây trên các mảng nhỏ (100 phần tử):

    for-loop + in                 384,426
    filter + in                   192,066
    for-loops                     159,137
    filter + includes             104,068
    filter + indexOf               71,598
    filter + has (this)            43,531 (this answer)
    filter + has (arrow function)  35,588

2
intersect([1,2,2,3], [2,3,4,5])trả lại [2, 2, 3].
SeregPie

1
@SeregPie Chính xác. Theo nhận xét "Trả về các phần tử của mảng a cũng nằm trong b"
le_m

Tuy nhiên, câu trả lời chất lượng là việc sử dụng Bộ thay đổi về cơ bản kết quả do câu hỏi của op chỉ hỏi về giao điểm mảng và không đề cập / quy định về cách xử lý trùng lặp. Nhút nhát, câu trả lời này có thể mang lại kết quả bất ngờ khi tồn tại trùng lặp.
Madbreaks

1
Thích nó, nhưng bạn đã thêm một chức năng không cần thiết với "bộ lọc + bao gồm". thử a.filter(b.includes). Nó sẽ chạy nhanh hơn đáng kể (giống như nâng cấp chức năng của bạn).
SEoF

11

Làm thế nào về việc chỉ sử dụng mảng kết hợp?

function intersect(a, b) {
    var d1 = {};
    var d2 = {};
    var results = [];
    for (var i = 0; i < a.length; i++) {
        d1[a[i]] = true;
    }
    for (var j = 0; j < b.length; j++) {
        d2[b[j]] = true;
    }
    for (var k in d1) {
        if (d2[k]) 
            results.push(k);
    }
    return results;
}

biên tập:

// new version
function intersect(a, b) {
    var d = {};
    var results = [];
    for (var i = 0; i < b.length; i++) {
        d[b[i]] = true;
    }
    for (var j = 0; j < a.length; j++) {
        if (d[a[j]]) 
            results.push(a[j]);
    }
    return results;
}

1
Điều này chỉ có cơ hội nếu mảng của bạn chỉ chứa chuỗi hoặc số và nếu không có tập lệnh nào trong trang của bạn bị rối Object.prototype.
Tim Xuống

2
Ví dụ của OP đã sử dụng các số và nếu một tập lệnh bị rối với Object.prototype thì tập lệnh sẽ được viết lại hoặc xóa.
Steven Huwig

Bạn không cần cả (d1) và (d2). Tạo (d2), sau đó lặp qua (a) thay vì lặp qua (d1).
StanleyH

Nên d[b[i]] = true;thay vì d[b[j]] = true;( ikhông j). Nhưng chỉnh sửa đòi hỏi 6 ký tự.
Izhaki

@Izhaki cảm ơn, đã sửa. (Đã thêm // nhận xét để nhận được yêu cầu chỉnh sửa tối thiểu.)
Steven Huwig

8
  1. Sắp xếp nó
  2. kiểm tra từng cái một từ chỉ số 0, tạo mảng mới từ đó.

Một cái gì đó như thế này, Không được thử nghiệm mặc dù.

function intersection(x,y){
 x.sort();y.sort();
 var i=j=0;ret=[];
 while(i<x.length && j<y.length){
  if(x[i]<y[j])i++;
  else if(y[j]<x[i])j++;
  else {
   ret.push(x[i]);
   i++,j++;
  }
 }
 return ret;
}

alert(intersection([1,2,3], [2,3,4,5]));

PS: Thuật toán chỉ dành cho Số và Chuỗi bình thường, giao điểm của mảng đối tượng tùy ý có thể không hoạt động.


3
Sắp xếp sẽ không nhất thiết phải giúp cho các mảng của các đối tượng tùy ý
Tim Down

Nếu mảng không được sắp xếp, cần lặp lại khoảng 1.000.000 lần khi bạn giao nhau 1000 mảng dài x 1000 mảng dài
BẠN

Tôi nghĩ rằng bạn đã bỏ lỡ quan điểm của tôi, đó là các đối tượng tùy ý trong JavaScript không có thứ tự sắp xếp tự nhiên, có nghĩa là việc sắp xếp một mảng các đối tượng tùy ý sẽ không dẫn đến việc các đối tượng bằng nhau được nhóm lại. Thật không tốt khi có một thuật toán hiệu quả mà không hoạt động.
Tim Down

À xin lỗi, tôi đã bỏ lỡ "các đối tượng tùy ý", vâng, bạn nói đúng. những đối tượng đó không thể sắp xếp nó và thuật toán có thể không hoạt động trên những đối tượng đó.
BẠN

8

Hiệu suất triển khai của @ atk cho các mảng nguyên thủy được sắp xếp có thể được cải thiện bằng cách sử dụng .pop thay vì .shift.

function intersect(array1, array2) {
   var result = [];
   // Don't destroy the original arrays
   var a = array1.slice(0);
   var b = array2.slice(0);
   var aLast = a.length - 1;
   var bLast = b.length - 1;
   while (aLast >= 0 && bLast >= 0) {
      if (a[aLast] > b[bLast] ) {
         a.pop();
         aLast--;
      } else if (a[aLast] < b[bLast] ){
         b.pop();
         bLast--;
      } else /* they're equal */ {
         result.push(a.pop());
         b.pop();
         aLast--;
         bLast--;
      }
   }
   return result;
}

Tôi đã tạo một điểm chuẩn bằng jsPerf: http://bit.ly/P9FrZK . Nó nhanh hơn khoảng ba lần để sử dụng .pop.


1
Cũng như một ghi chú bên lề cho người khác - điều này sẽ chỉ hoạt động cho các số, không phải chuỗi.
Izhaki

Lưu ý rằng nếu bạn thay thế a[aLast] > b[bLast]bằng a[aLast].localeCompare(b[bLast]) > 0(và tương tự với else ifbên dưới) thì điều này sẽ hoạt động trên chuỗi.
andrew

1
Chênh lệch tốc độ phụ thuộc vào kích thước của các mảng vì .poplà O (1) và .shift()là O (n)
Esailija

8

Sử dụng jQuery :

var a = [1,2,3];
var b = [2,3,4,5];
var c = $(b).not($(b).not(a));
alert(c);

8
Điều này cũng có thể được viết dưới dạng c = $(b).filter(a);, nhưng tôi không khuyên bạn nên dựa vào jQuery cho loại thao tác mảng này vì tài liệu chỉ đề cập rằng nó hoạt động cho các phần tử.
Stryner

1
Điều này không trả lời câu hỏi của op: "Mã đơn giản nhất, không có thư viện ..."
Madbreaks

7

Đối với các mảng chỉ chứa các chuỗi hoặc số, bạn có thể thực hiện một số thứ với cách sắp xếp, theo một số câu trả lời khác. Đối với trường hợp chung của các mảng của các đối tượng tùy ý, tôi không nghĩ bạn có thể tránh làm điều đó một cách lâu dài. Sau đây sẽ cung cấp cho bạn giao điểm của bất kỳ số lượng mảng nào được cung cấp làm tham số cho arrayIntersection:

var arrayContains = Array.prototype.indexOf ?
    function(arr, val) {
        return arr.indexOf(val) > -1;
    } :
    function(arr, val) {
        var i = arr.length;
        while (i--) {
            if (arr[i] === val) {
                return true;
            }
        }
        return false;
    };

function arrayIntersection() {
    var val, arrayCount, firstArray, i, j, intersection = [], missing;
    var arrays = Array.prototype.slice.call(arguments); // Convert arguments into a real array

    // Search for common values
    firstArray = arrays.pop();
    if (firstArray) {
        j = firstArray.length;
        arrayCount = arrays.length;
        while (j--) {
            val = firstArray[j];
            missing = false;

            // Check val is present in each remaining array 
            i = arrayCount;
            while (!missing && i--) {
                if ( !arrayContains(arrays[i], val) ) {
                    missing = true;
                }
            }
            if (!missing) {
                intersection.push(val);
            }
        }
    }
    return intersection;
}

arrayIntersection( [1, 2, 3, "a"], [1, "a", 2], ["a", 1] ); // Gives [1, "a"]; 

Điều này chỉ hoạt động trong trường hợp nhận dạng đối tượng là hình thức bình đẳng duy nhất.
Steven Huwig

Vâng, nhưng tôi nghĩ đó là điều có vẻ tự nhiên đối với hầu hết mọi người. Việc cắm vào một chức năng thay thế để thực hiện một bài kiểm tra đẳng thức khác nhau cũng không quan trọng.
Tim Xuống

Tôi nghĩ rằng bạn đang vô tình tạo ra một biến toàn cục FirstArr trong ví dụ của bạn.
Jason Jackson

@JasonJackson: Bạn nói đúng, cảm ơn. Rõ ràng đã thay đổi suy nghĩ của tôi về việc có nên gọi biến firstArrhay firstArraykhông và không cập nhật tất cả các tham chiếu. Đã sửa.
Tim Down

7

Nó khá ngắn khi sử dụng ES2015 và Bộ. Chấp nhận các giá trị giống như mảng như Chuỗi và loại bỏ trùng lặp.

let intersection = function(a, b) {
  a = new Set(a), b = new Set(b);
  return [...a].filter(v => b.has(v));
};

console.log(intersection([1,2,1,2,3], [2,3,5,4,5,3]));

console.log(intersection('ccaabbab', 'addb').join(''));


Chuyển đổi từ Đặt thành mảng với [... a] sẽ xóa các mục trùng lặp, ý kiến ​​hay, cảm ơn
V-SHY

1
Giải pháp này đã được cung cấp hai lần trước của bạn.
Madbreaks

7

Bạn có thể sử dụng một Setnhư thisArgcủa Array#filtervà mất Set#hasnhư gọi lại.

function intersection(a, b) {
    return a.filter(Set.prototype.has, new Set(b));
}

console.log(intersection([1, 2, 3], [2, 3, 4, 5]));


Tôi không biết tại sao điều này không có nhiều phiếu bầu hơn. Nó rõ ràng là câu trả lời tốt nhất.
Paul Rooney

5

Một điều chỉnh nhỏ đến mức nhỏ nhất ở đây ( giải pháp bộ lọc / indexOf ), cụ thể là tạo một chỉ mục của các giá trị trong một trong các mảng bằng cách sử dụng một đối tượng JavaScript, sẽ giảm thời gian tuyến tính từ O (N * M) xuống "có thể". nguồn1 nguồn2

function intersect(a, b) {
  var aa = {};
  a.forEach(function(v) { aa[v]=1; });
  return b.filter(function(v) { return v in aa; });
}

Đây không phải là giải pháp đơn giản nhất (mã nhiều hơn bộ lọc + indexOf ), cũng không phải là giải pháp nhanh nhất (có thể chậm hơn bởi một yếu tố không đổi so với giao lộ_safe () ), nhưng có vẻ như là một sự cân bằng khá tốt. Đó là về mặt rất đơn giản, trong khi cung cấp hiệu suất tốt, và nó không yêu cầu đầu vào được sắp xếp trước.


5

Một cách tiếp cận được lập chỉ mục khác có thể xử lý bất kỳ số lượng mảng nào cùng một lúc:

// Calculate intersection of multiple array or object values.
function intersect (arrList) {
    var arrLength = Object.keys(arrList).length;
        // (Also accepts regular objects as input)
    var index = {};
    for (var i in arrList) {
        for (var j in arrList[i]) {
            var v = arrList[i][j];
            if (index[v] === undefined) index[v] = 0;
            index[v]++;
        };
    };
    var retv = [];
    for (var i in index) {
        if (index[i] == arrLength) retv.push(i);
    };
    return retv;
};

Nó chỉ hoạt động cho các giá trị có thể được đánh giá dưới dạng chuỗi và bạn nên chuyển chúng dưới dạng một mảng như:

intersect ([arr1, arr2, arr3...]);

... nhưng nó chấp nhận một cách trong suốt các đối tượng như là tham số hoặc là bất kỳ phần tử nào được giao nhau (luôn trả về mảng các giá trị chung). Ví dụ:

intersect ({foo: [1, 2, 3, 4], bar: {a: 2, j:4}}); // [2, 4]
intersect ([{x: "hello", y: "world"}, ["hello", "user"]]); // ["hello"]

EDIT: Tôi chỉ nhận thấy rằng, theo một cách nào đó, hơi lỗi.

Đó là: Tôi đã mã hóa nó nghĩ rằng các mảng đầu vào không thể chứa các lần lặp lại (như ví dụ được cung cấp không).

Nhưng nếu mảng đầu vào xảy ra có chứa sự lặp lại, điều đó sẽ tạo ra kết quả sai. Ví dụ (sử dụng thực hiện bên dưới):

intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]);
// Expected: [ '1' ]
// Actual: [ '1', '3' ]

May mắn là điều này rất dễ khắc phục bằng cách thêm chỉ mục cấp hai. Đó là:

Thay đổi:

        if (index[v] === undefined) index[v] = 0;
        index[v]++;

bởi:

        if (index[v] === undefined) index[v] = {};
        index[v][i] = true; // Mark as present in i input.

... và:

         if (index[i] == arrLength) retv.push(i);

bởi:

         if (Object.keys(index[i]).length == arrLength) retv.push(i);

Ví dụ hoàn chỉnh:

// Calculate intersection of multiple array or object values.
function intersect (arrList) {
    var arrLength = Object.keys(arrList).length;
        // (Also accepts regular objects as input)
    var index = {};
    for (var i in arrList) {
        for (var j in arrList[i]) {
            var v = arrList[i][j];
            if (index[v] === undefined) index[v] = {};
            index[v][i] = true; // Mark as present in i input.
        };
    };
    var retv = [];
    for (var i in index) {
        if (Object.keys(index[i]).length == arrLength) retv.push(i);
    };
    return retv;
};

intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]); // [ '1' ]

2
Đây là câu trả lời tốt nhất với một sửa đổi nhỏ. Sau khi var v = thêm dòng if (typeof v == 'function') continue;và nó sẽ bỏ qua việc thêm chức năng vào kết quả. Cảm ơn!
Zsolti

Cảm ơn @Zsolti. Tôi không thêm đề xuất của bạn vì có chức năng (và cách chúng tôi muốn xử lý) nằm ngoài phạm vi của câu hỏi ban đầu. Nhưng hãy xem chỉnh sửa của tôi: Nếu bạn có thể có sự lặp lại trong mảng đầu vào của mình, thì việc thực hiện ban đầu là lỗi. Tôi đã sửa nó trong chỉnh sửa của tôi. Mặt khác, nếu bạn biết chắc chắn rằng sẽ không có sự lặp lại, thì việc thực hiện ban đầu rẻ hơn một chút.
bitifet

... về các hàm, chúng cũng có thể được giao nhau: Nếu chúng ta phát hiện ra chúng như @Zsolti nói (với if (typeof v == 'function'), thì chúng ta có thể sử dụng chuỗi của nó ( v.toString()) làm khóa cho chỉ mục. Nhưng, chúng ta cần phải làm gì đó để giữ nguyên. Cách dễ nhất để làm như vậy chỉ đơn giản là gán hàm gốc là giá trị thay vì giá trị boolean đơn giản. Nhưng, trong trường hợp đó, deindexaton mới nhất cũng nên được thay đổi để phát hiện điều kiện này và khôi phục đúng giá trị (hàm).
bitifet

Điều này có thể nhanh đến mức nào với 30 mảng với 100 phần tử .. việc sử dụng CPU như thế nào?
viện trợ

5

Bạn có thể sử dụng (cho tất cả các trình duyệt trừ IE):

const intersection = array1.filter(element => array2.includes(element));

hoặc cho IE:

const intersection = array1.filter(element => array2.indexOf(element) !== -1);

Sẽ thật tuyệt nếu bạn có thể biến điều đó thành một chức năng
avalanche1

@ avalanche1 const ngã tư = (a1, a2) => a1.filter (e => a2.includes (e));
jota3

4
function intersection(A,B){
var result = new Array();
for (i=0; i<A.length; i++) {
    for (j=0; j<B.length; j++) {
        if (A[i] == B[j] && $.inArray(A[i],result) == -1) {
            result.push(A[i]);
        }
    }
}
return result;
}

4

Với một số hạn chế về dữ liệu của bạn, bạn có thể làm điều đó trong thời gian tuyến tính !

Đối với các số nguyên dương : sử dụng một mảng ánh xạ các giá trị thành một boolean "nhìn thấy / không nhìn thấy".

function intersectIntegers(array1,array2) { 
   var seen=[],
       result=[];
   for (var i = 0; i < array1.length; i++) {
     seen[array1[i]] = true;
   }
   for (var i = 0; i < array2.length; i++) {
     if ( seen[array2[i]])
        result.push(array2[i]);
   }
   return result;
}

Có một kỹ thuật tương tự cho các đối tượng : lấy một khóa giả, đặt nó thành "true" cho từng phần tử trong mảng1, sau đó tìm khóa này trong các phần tử của mảng2. Dọn dẹp khi bạn hoàn thành.

function intersectObjects(array1,array2) { 
   var result=[];
   var key="tmpKey_intersect"
   for (var i = 0; i < array1.length; i++) {
     array1[i][key] = true;
   }
   for (var i = 0; i < array2.length; i++) {
     if (array2[i][key])
        result.push(array2[i]);
   }
   for (var i = 0; i < array1.length; i++) {
     delete array1[i][key];
   }
   return result;
}

Tất nhiên bạn cần chắc chắn rằng khóa không xuất hiện trước đó, nếu không bạn sẽ phá hủy dữ liệu của mình ...


Nhân tiện, điều này có thể dễ dàng được mở rộng để xen kẽ bất kỳ số lượng mảng nào: thay thế boolean bằng số nguyên và tăng mỗi lần nhìn thấy: bạn có thể dễ dàng đọc giao điểm trên vòng cuối cùng.
tarulen

Giải pháp thú vị, tôi thích nó. Hầu hết các giải pháp khác là O (n ^ 2), nhưng đây là O (n). Tôi đã thêm mã số nguyên vào fiddle hiệu suất của ericP ở đây jsfiddle.net/321juyLu/2 . Nó đến thứ 3, tôi thích :)
rmcsharry

3

Tôi sẽ đóng góp với những gì đã làm việc tốt nhất cho tôi:

if (!Array.prototype.intersect){
Array.prototype.intersect = function (arr1) {

    var r = [], o = {}, l = this.length, i, v;
    for (i = 0; i < l; i++) {
        o[this[i]] = true;
    }
    l = arr1.length;
    for (i = 0; i < l; i++) {
        v = arr1[i];
        if (v in o) {
            r.push(v);
        }
    }
    return r;
};
}

3

"IndexOf" cho IE 9.0, chrome, firefox, opera,

    function intersection(a,b){
     var rs = [], x = a.length;
     while (x--) b.indexOf(a[x])!=-1 && rs.push(a[x]);
     return rs.sort();
    }

intersection([1,2,3], [2,3,4,5]);
//Result:  [2,3]

2

'use strict'

// Example 1
function intersection(a1, a2) {
    return a1.filter(x => a2.indexOf(x) > -1)
}

// Example 2 (prototype function)
Array.prototype.intersection = function(arr) {
    return this.filter(x => arr.indexOf(x) > -1)
} 

const a1 = [1, 2, 3]
const a2 = [2, 3, 4, 5]

console.log(intersection(a1, a2))
console.log(a1.intersection(a2))


2

Một cách tiếp cận chức năng với ES2015

Một cách tiếp cận chức năng phải xem xét chỉ sử dụng các chức năng thuần túy mà không có tác dụng phụ, mỗi chức năng chỉ liên quan đến một công việc duy nhất.

Những hạn chế này tăng cường khả năng kết hợp và tái sử dụng của các chức năng liên quan.

// small, reusable auxiliary functions

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


// intersection

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


// mock data

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


// run it

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

Xin lưu ý rằng Setloại bản địa được sử dụng, có hiệu suất tra cứu thuận lợi.

Tránh trùng lặp

Rõ ràng các mục xuất hiện lặp đi lặp lại từ lần đầu tiên Arrayđược bảo tồn, trong khi mục thứ hai Arrayđược nhân đôi. Điều này có thể hoặc có thể không phải là hành vi mong muốn. Nếu bạn cần một kết quả duy nhất, chỉ cần áp dụng dedupecho đối số đầu tiên:

// auxiliary functions

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


// intersection

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


// de-duplication

const dedupe = comp(afrom) (createSet);


// mock data

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


// unique result

console.log( intersect(dedupe(xs)) (ys) );

Tính giao điểm của bất kỳ số Arrays nào

Nếu bạn muốn tính giao điểm của một số Arrays tùy ý chỉ cần soạn intersectvới foldl. Đây là một chức năng tiện lợi:

// auxiliary functions

const apply = f => x => f(x);
const uncurry = f => (x, y) => f(x) (y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);


// intersection

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


// intersection of an arbitrarily number of Arrays

const intersectn = (head, ...tail) => foldl(intersect) (head) (tail);


// mock data

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


// run

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


Chức năng ấn tượng: phải thực hiện hai lần để xác nhận rằng đó không phải là Haskell. Các nitpick duy nhất là: (expr ? true : false)là dư thừa. Sử dụng chỉ exprkhi booleans thực tế không cần thiết, chỉ cần trung thực / giả.
jose_castro_arnaud

2

Để đơn giản:

// Usage
const intersection = allLists
  .reduce(intersect, allValues)
  .reduce(removeDuplicates, []);


// Implementation
const intersect = (intersection, list) =>
  intersection.filter(item =>
    list.some(x => x === item));

const removeDuplicates = (uniques, item) =>
  uniques.includes(item) ? uniques : uniques.concat(item);


// Example Data
const somePeople = [bob, doug, jill];
const otherPeople = [sarah, bob, jill];
const morePeople = [jack, jill];

const allPeople = [...somePeople, ...otherPeople, ...morePeople];
const allGroups = [somePeople, otherPeople, morePeople];

// Example Usage
const intersection = allGroups
  .reduce(intersect, allPeople)
  .reduce(removeDuplicates, []);

intersection; // [jill]

Những lợi ích:

  • bụi bẩn đơn giản
  • trung tâm dữ liệu
  • làm việc cho số lượng danh sách tùy ý
  • làm việc cho độ dài tùy ý của danh sách
  • làm việc cho các loại giá trị tùy ý
  • làm việc cho thứ tự sắp xếp tùy ý
  • giữ lại hình dạng (thứ tự xuất hiện đầu tiên trong bất kỳ mảng nào)
  • xuất cảnh sớm nếu có thể
  • bộ nhớ an toàn, không bị xáo trộn với các nguyên mẫu Function / Array

Hạn chế:

  • sử dụng bộ nhớ cao hơn
  • sử dụng CPU cao hơn
  • đòi hỏi một sự hiểu biết về giảm
  • đòi hỏi sự hiểu biết về luồng dữ liệu

Bạn sẽ không muốn sử dụng công cụ này cho công cụ 3D hoặc kernel, nhưng nếu bạn gặp vấn đề khi chạy ứng dụng này trong một ứng dụng dựa trên sự kiện, thiết kế của bạn có vấn đề lớn hơn.


2

.reduceđể xây dựng một bản đồ, và .filterđể tìm giao lộ. deletetrong phạm vi .filtercho phép chúng ta xử lý mảng thứ hai như thể đó là một tập hợp duy nhất.

function intersection (a, b) {
  var seen = a.reduce(function (h, k) {
    h[k] = true;
    return h;
  }, {});

  return b.filter(function (k) {
    var exists = seen[k];
    delete seen[k];
    return exists;
  });
}

Tôi thấy cách tiếp cận này khá dễ dàng để lý do về. Nó thực hiện trong thời gian liên tục.


2

Đây có lẽ là cách đơn giản nhất, bên cạnh list1.filter (n => list2.includes (n))

var list1 = ['bread', 'ice cream', 'cereals', 'strawberry', 'chocolate']
var list2 = ['bread', 'cherry', 'ice cream', 'oats']

function check_common(list1, list2){
	
	list3 = []
	for (let i=0; i<list1.length; i++){
		
		for (let j=0; j<list2.length; j++){	
			if (list1[i] === list2[j]){
				list3.push(list1[i]);				
			}		
		}
		
	}
	return list3
	
}

check_common(list1, list2) // ["bread", "ice cream"]


điều này có độ phức tạp thời gian O (nm) ... điều này có thể được giải quyết trong O (n + m)
alchuang

2

Nếu bạn cần phải có nó xử lý giao nhau nhiều mảng:

const intersect = (a, b, ...rest) => {
  if (rest.length === 0) return [...new Set(a)].filter(x => new Set(b).has(x));
  return intersect(a, intersect(b, ...rest));
};

console.log(intersect([1,2,3,4,5], [1,2], [1, 2, 3,4,5], [2, 10, 1])) // [1,2]


Nhưng giải pháp này nhanh như thế nào đối với 30 mảng với 100 phần tử?
viện trợ

Điều này không sử dụng gì ngoài các phương thức Javascript và do đó VM sẽ chạy mã miễn phí để tối ưu hóa nó đến mức có thể. Tôi khá tích cực rằng không tồn tại giải pháp nào nhanh hơn nếu bạn đang chạy phiên bản V8 hiện đại này so với độ tuổi của nhận xét này.
Belfordz

2

Phong cách ES6 đơn giản.

const intersection = (a, b) => {
  const s = new Set(b);
  return a.filter(x => s.has(x));
};

Thí dụ:

intersection([1, 2, 3], [4, 3, 2]); // [2, 3]

2

Tôi đã viết một hàm intesection thậm chí có thể phát hiện giao điểm của mảng các đối tượng dựa trên thuộc tính cụ thể của các đối tượng đó.

Ví dụ,

if arr1 = [{id: 10}, {id: 20}]
and arr2 =  [{id: 20}, {id: 25}]

và chúng tôi muốn giao nhau dựa trên thuộc idtính, thì đầu ra phải là:

[{id: 20}]

Như vậy, hàm cho cùng (lưu ý: mã ES6) là:

const intersect = (arr1, arr2, accessors = [v => v, v => v]) => {
    const [fn1, fn2] = accessors;
    const set = new Set(arr2.map(v => fn2(v)));
    return arr1.filter(value => set.has(fn1(value)));
};

và bạn có thể gọi hàm là:

intersect(arr1, arr2, [elem => elem.id, elem => elem.id])

Cũng lưu ý: hàm này tìm thấy giao điểm coi mảng đầu tiên là mảng chính và do đó kết quả giao cắt sẽ là của mảng chính.


2

Hãy nghĩ rằng điều này sẽ nhanh hơn với thời gian O (mảng1 + mảng2) giả sử map.has () là ~ O (1). Xin vui lòng sửa cho tôi nếu sai.

const intersection = (a1, a2) => {
  let map = new Map();
  let result = []
  for (let i of a1) {
    if (!map.has(i)) map.set(i, true);
  }
  for (let i of a2) {
    if (map.has(i)) result.push(i)
  }
  return result;
}


1

Đây là cách triển khai underscore.js :

_.intersection = function(array) {
  if (array == null) return [];
  var result = [];
  var argsLength = arguments.length;
  for (var i = 0, length = array.length; i < length; i++) {
    var item = array[i];
    if (_.contains(result, item)) continue;
    for (var j = 1; j < argsLength; j++) {
      if (!_.contains(arguments[j], item)) break;
    }
    if (j === argsLength) result.push(item);
  }
  return result;
};

Nguồn: http://underscorejs.org/docs/underscore.html#section-62


Một tài liệu tham khảo không tồi nếu có sẵn undesrcore
Dimitrios Mistriotis
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.