2012-10-30 10 views
33
_.difference([], []) 

phương pháp này hoạt động tốt khi tôi đang gặp kiểu dữ liệu nguyên thủy nhưsử dụng phương pháp “khác biệt” gạch về mảng các đối tượng

var a = [1,2,3,4]; 
var b = [2,5,6]; 

_.difference(a,b) gọi trả [1,3,4]

nhưng trong trường hợp tôi đang sử dụng đối tượng như

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}]; 
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}]; 

dường như không hoạt động

+0

tương tự (không trùng lặp) để http://stackoverflow.com/questions/8672383/how-to-use-underscores-intersection-on-objects – drzaus

+0

và nếu bạn đến đây tìm kiếm sự khác biệt giữa hai vật (tức là delta của chúng, không phải giữa các mảng của các đối tượng), không có gì được tích hợp sẵn trong Underscore nhưng bạn có thể thử http://stackoverflow.com/a/25651677/1037948 – drzaus

+1

Lưu ý rằng _difference (a, b) sẽ trả về [1, 3,4]. Phương thức _.difference sẽ trả về các phần tử của mảng đầu tiên không có trong __other arrays__ – Aegis

Trả lời

12

Lý do đơn giản là đối tượng có cùng nội dung không phải là cùng một đối tượng, ví dụ:

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}]; 
a.indexOf({'id':1, 'value':10}) 

Nó sẽ không trở về 0 nhưng -1 bởi vì chúng tôi đang tìm kiếm một đối tượng khác nhau

Xem mã nguồn http://underscorejs.org/underscore.js, _.difference sử dụng _.contains

_.difference = function(array) { 
    var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); 
    return _.filter(array, function(value){ return !_.contains(rest, value); }); 
}; 

_.contains cuối cùng sử dụng indexOf do đó sẽ không tìm thấy đối tượng trừ khi chúng trỏ đến cùng một đối tượng.

Bạn có thể cải thiện các dấu gạch dưới _.contains bằng vòng lặp qua tất cả các mặt hàng và gọi một so sánh gọi lại, trong đó bạn sẽ có thể để vượt qua sự khác biệt hoặc chứa hàm hay bạn có thể kiểm tra this version which improves contains methods

+0

là có cách nào để so sánh đối tượng với giá trị của nó – Nas

+1

@Nas Xem chỉnh sửa hoặc kiểm tra https: // github này.com/panbhag/gạch dưới chứa/blob/master/underscore-contains.js –

52

thử này trên cho kích thước cho việc tìm kiếm sự khác biệt của một mảng của các đối tượng:

var test = [{a: 1},{b: 2}]; 
var test2 = [{a: 1}]; 

_.filter(test, function(obj){ return !_.findWhere(test2, obj); }); 
+0

Bạn đã đóng đinh nó. Thanh lịch. –

+0

có thể (gần như?) Được viết lại dưới dạng '_.reject (test, _.matches (test2 [0]))' nếu bạn chỉ khác với một đối tượng – drzaus

+0

Và nếu bạn muốn lọc theo một id sử dụng: '' '_.filter (kiểm tra, hàm (obj) {return! _. findWhere (test2, {id: obj.id});});' '' – MrWashinton

28

trong khi trả lời chấp nhận là đúng, và câu trả lời khác cho ý tưởng tốt là tốt, có một tùy chọn bổ sung đó là khá dễ dàng để thực hiện với dấu gạch dưới.

Giải pháp này dựa vào từng đối tượng có ID duy nhất, nhưng trong nhiều trường hợp, điều này sẽ đúng và bạn có thể nhận được sự khác biệt của hai mảng đối tượng chỉ trong hai dòng mã.

Sử dụng phương pháp "nhổ" của gạch dưới, bạn có thể nhanh chóng xây dựng một mảng của tất cả các ID trong bộ nguồn của bạn và bộ đích. Từ đó, tất cả các phương thức mảng của gạch dưới sẽ hoạt động, khác biệt, công đoàn, giao điểm, v.v ...

Sau khi thao tác, việc lấy danh sách các đối tượng từ danh sách nguồn mà bạn mong muốn là tầm thường. Dưới đây là một ví dụ:

Verbose:

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}]; 
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}]; 

var arr1 = _.pluck(a, "id"); 
var arr2 = _.pluck(b, "id"); 
var diff = _.difference(arr1, arr2); 
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; }); 

hay, ngắn gọn hơn:

var diff = _.difference(_.pluck(a, "id"), _.pluck(b, "id")); 
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; }); 

Dĩ nhiên, kỹ thuật này tương tự có thể được mở rộng để sử dụng với bất kỳ phương pháp mảng.

+4

Câu trả lời hay, tôi thích kontr0l nhưng Angular thích thêm một thuộc tính hashkey vào các đối tượng để nó làm cho nó khó khăn để so sánh toàn bộ đối tượng nếu những gì bạn so sánh với không có hàm băm. Với câu trả lời của bạn, tôi có thể so sánh trên ID một mình hoạt động hoàn hảo. – Batman

+0

** cảnh báo ** nhổ không tồn tại, đọc [changelog lodash 4.0.0] này (http://stackoverflow.com/questions/35136306/what-happened-to-lodash-pluck) bây giờ bạn có thể sử dụng ** _.map ** – maliness

+0

@maliness, để làm rõ, nhổ không tồn tại cho ** lodash **, nhưng nó * hiện * tồn tại dưới dấu gạch chân, đó là câu hỏi được nhắc tới. –

0

Hãy tha thứ cho tôi vì nhảy vào cuối năm ở đây, nhưng điều này có thể giúp:

array_of_objects = 
    // return the non-matching items (without the expected properties) 
    _.difference(array_of_objects, 
     // filter original list for items with expected properties 
     _.where(
      // original list 
      array_of_objects, 
      // expected properties 
      {'id':1, 'value':10} 
     ) 
    ) 
+0

Thụt lề mã của bạn bằng cách đặt 4 dấu cách đằng sau nó hoặc bằng cách đặt nó vào trong các ký tự ''. – Joel

+1

Cảm ơn! Đã được một thời gian ngắn. – Gauss156

3

tôi thực sự có thể tưởng tượng tình huống mà tôi muốn sử dụng cách tiếp cận @ kontr0l hơn cái gì khác, nhưng bạn phải hiểu rằng phương pháp này là bậc hai, vì vậy về cơ bản mã này là một trừu tượng cho cách tiếp cận ngây thơ - lặp qua tất cả các giá trị trong hai mảng.

Có phương pháp tiếp cận tốt hơn so với bậc hai, tôi sẽ không sử dụng ở đây bất kỳ ký hiệu O lớn, nhưng đây là hai phương pháp chính, cả hai đều tốt hơn sau đó ngây thơ một:

  • lặp thông qua một trong những mảng và kiểm tra cho sự tồn tại trong mảng thứ hai được sắp xếp bằng cách sử dụng tìm kiếm nhị phân.
  • đặt giá trị vào tập hợp/băm/từ điển/bạn đặt tên cho nó.

Như đã được đề cập, cách tiếp cận đầu tiên có thể được áp dụng cho các đối tượng nếu bạn thực hiện lại phương pháp chuẩn difference với sử dụng phương pháp tương tự linh hoạt hơn indexOf.

Với cách tiếp cận thứ hai, chúng tôi có thể chạm vào tường với thực tế là, đến tháng 2 năm 2015, chỉ các trình duyệt hiện đại mới hỗ trợ Sets. Khi băm (tốt, các đối tượng) trong javascript, chúng có thể chỉ có các khóa kiểu chuỗi, vì vậy bất kỳ đối tượng nào được gọi là khóa đầu tiên được chuyển đổi thông qua phương thức toString. Vì vậy, chúng tôi cần phải cung cấp một số => correspondece. Trên thực tế trong hầu hết các trường hợp, nó khá đơn giản, ví dụ, ví dụ cụ thể của bạn thư từ như vậy có thể chỉ là String(obj.id).

Có thư như vậy, chúng ta cũng có thể sử dụng lodas sau/cách tiếp cận undercore:

var idsA = _.pluck(a, 'id'); 
var idsB = _.pluck(b, 'id'); 

// actually here we can stop in some cases, because 
// quite often we need to identify object, but not the object itself - 
// for instance to send some ids through remote API. 
var intersect = _.intersection(idsA, idsB); 

//to be 100% sure you get the idea, here we assume that object having equal ids are treated as equal, so does not really matter which of arrays we'll iterate: 

var dictA = _.object(idsA, a); // now we can find a by id faster then with _.find 
var intersectObj = intersect.map(function(id) {return dictA[id}) 

Nhưng mua thừa nhận hạn chế hơi khắt khe hơn - mà chúng ta có thể xây dựng tương ứng giữa các đối tượng thiết lập của chúng tôi và các số tự nhiên chúng ta có thể xây dựng efficent thậm chí nhiều hơn thuật toán, tức là tất cả các id của chúng tôi là số nguyên không âm - chúng tôi có thể sử dụng thuật toán hiệu quả hơn.

Bí quyết là để thực hiện thiết lập bằng cách giới thiệu hai mảng helper theo cách này:

var naturalSet = function (arr) { 
    var sparse = []; 
    var dense = []; 

    var contains = function (i) { 
     var res = sparse[i] < dense.length && dense[sparse[i]] == i; 
     return res; 
    } 

    var add = function (v) { 
     if (!contains(v)) { 
      sparse[v] = dense.length; 
      dense.push(v); 
     } 
    } 

    arr.forEach(add); 

    return { 
     contains: contains, 
     toArray: function() { 
      return dense 
     }, 
     _getDense: function() { 
      return dense 
     }, 
     _getSparse: function() { 
      return sparse 
     } 
    } 
} 

Sau đó, chúng ta có thể giới thiệu thiết lập với bản đồ để naturalSet:

var set = function (arr, valueOf) { 
    var natSet = naturalSet(arr.map(valueOf)); 
    return { 
     contains: function (item) { 
      return natSet.contains(valueOf(item)) 
     }, 
     toArray: function() { 
      var sparse = natSet._getSparse(); 
      var res = natSet._getDense().map(function (i) { 
       return arr[sparse[i]]; 
      }); 
      return res; 
     } 
    } 
} 

và cuối cùng, chúng ta có thể giới thiệu giao lộ:

var intersection = function(arr1, arr2, valueOf) { 
    return set(arr2.filter(set(arr1, valueOf).contains), valueOf).toArray(); 
} 

Vì vậy, dựa vào cấu trúc dữ liệu bạn đang làm việc có thể p bạn đôi khi.

2
without using underscorejs, 
here is the pretty simple method i got solution ... 

a = [{'key':'123'},{'key':'222'},{'key':'333'}] 
b = [{'key':'123'},{'key':'222'}] 

var diff = a.filter(function(item1) { 
    for (var i in b) { 
    if (item1.key === b[i].key) { return false; } 
    }; 
    return true; 
}); 
console.log('result',diff)