web-dev-qa-db-ja.com

オブジェクトの配列でアンダースコアの「差分」メソッドを使用する

__.difference([], [])
_

このメソッドは、次のようなプリミティブ型のデータがあるときにうまく機能します

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

_.difference(a,b)呼び出しは_[1,3,4]_を返します

しかし、私がのようなオブジェクトを使用している場合

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

うまくいかないようです

38
Nas

理由は、同じコンテンツを持つオブジェクトが同じオブジェクトではないということです。

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

別のオブジェクトを検索しているため、0ではなく-1を返します。

ソースコードを参照してください http://underscorejs.org/underscore.js_.difference_.containsを使用します

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

そして_.containsは最終的にindexOfを使用するため、同じオブジェクトを指していなければオブジェクトを見つけられません。

アンダースコア_.containsを改善するには、すべてのアイテムをループして比較コールバックを呼び出します。これは、差分または包含関数に渡すことができます。または、このバージョンを確認する メソッドを含む

15
Anurag Uniyal

オブジェクトの配列の違いを見つけるために、サイズでこれを試してください:

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

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

受け入れられた答えは正解であり、他の答えも良いアイデアを提供しますが、アンダースコアを使用して実装するのが非常に簡単な追加オプションがあります。

このソリューションは、一意のIDを持つ各オブジェクトに依存していますが、多くの場合これは当てはまり、2行のコードでオブジェクトの2つの配列の違いを取得できます。

アンダースコアの「pluck」メソッドを使用すると、ソースセットとターゲットセットのすべてのIDの配列をすばやく構築できます。そこから、アンダースコアのすべての配列メソッドが機能します。差分、結合、交差など。

操作後、ソースリストから目的のオブジェクトのリストを取得するのは簡単です。以下に例を示します。

冗長:

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; });

または、より簡潔に:

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

もちろん、これと同じ手法を拡張して、任意の配列メソッドで使用できます。

30
Clayton Gulick
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)

実際、@ kontr0lアプローチを他の方法よりも使用したい状況を想像できますが、このアプローチは2次式であるため、基本的にこのコードは単純なアプローチの抽象化であり、2つの配列のすべての値を反復処理します。

二次式よりも優れたアプローチがありますが、ここでは大きなO表記は使用しませんが、主なアプローチは2つあり、どちらもナイーブなアプローチよりも優れています。

  • 配列の1つを反復処理し、バイナリ検索を使用して、ソートされた2番目の配列の存在を確認します。
  • set/hash/dictionary/nameに値を入れます。

すでに述べたように、differenceメソッドのより柔軟な類似物を使用して標準のindexOfメソッドを再実装する場合、オブジェクトに最初のアプローチを採用できます。

2番目のアプローチでは、2015年2月の時点で、最新のブラウザのみが Sets をサポートしているという事実を突き止めることができます。 javascriptのハッシュ(オブジェクト)では、文字列型のキーしか使用できないため、キーとして最初に呼び出されるオブジェクトはtoStringメソッドを使用して変換されます。したがって、いくつかの=>通信を提供する必要があります。実際には、ほとんどの場合、それは非常に簡単です。たとえば、特定の例では、そのような対応はString(obj.id)になります。

このような対応があるため、次のloda /アンダーコアアプローチも使用できます。

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})

ただし、セットオブジェクトと自然数との対応を構築できること、より効率的なアルゴリズムを構築できること、つまり、すべてのIDが非負の整数であるというわずかに厳しい制限を認めることを購入します。より効率的なアルゴリズムを使用できます。

この方法は、次のように2つのヘルパー配列を導入してセットを実装することです。

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
        }
    }
}

次に、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;
        }
    }
}

最後に、交差点を導入できます。

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

そのため、作業中のデータの構造に依存することで、役立つ場合があります。

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

var c = _.difference(a.map(e => e.id), b.map(e =>e.id));
var array = [];
array = a.map(e => {
   if(c.includes(e.id)){
     return e;
   }
}).filter(r=>r);
1
Anirudh

私が何かを逃さない限り、なぜこれらの答えがそんなに複雑なのか分かりませんか?

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

// Or use lodash _.differenceBy
const difference = (array1, array2, prop = 'id') =>
  array1.filter(item1 =>
    !array2.some(item2 =>
      item2[prop] === item1[prop],
    ),
  );
  
// In one array.
console.log(difference(a, b));

// Intersection.
console.log([...difference(a, b), ...difference(b, a)]);
0
Dominic

ここで遅くホッピングすることを許してください、しかしこれは助けになるかもしれません:

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
Gauss156