web-dev-qa-db-ja.com

オブジェクトを含む2つの配列の差と交差

いくつかのプロパティを持つオブジェクトを持つ2つの配列list1list2があります。 userIdはIDまたは一意のプロパティです。

list1 = [
    { userId: 1234, userName: 'XYZ'  }, 
    { userId: 1235, userName: 'ABC'  }, 
    { userId: 1236, userName: 'IJKL' },
    { userId: 1237, userName: 'WXYZ' }, 
    { userId: 1238, userName: 'LMNO' }
]

list2 = [
    { userId: 1235, userName: 'ABC'  },  
    { userId: 1236, userName: 'IJKL' },
    { userId: 1252, userName: 'AAAA' }
]

次の3つの操作を実行する簡単な方法を探しています。

  1. list1 operation list2は要素の共通部分を返す必要があります:

    [
        { userId: 1235, userName: 'ABC'  },
        { userId: 1236, userName: 'IJKL' }
    ]
    
  2. list1 operation list2は、list1では発生しないlist2のすべての要素のリストを返す必要があります。

    [
        { userId: 1234, userName: 'XYZ'  },
        { userId: 1237, userName: 'WXYZ' }, 
        { userId: 1238, userName: 'LMNO' }
    ]
    
  3. list2 operation list1は、list2では発生しないlist1からの要素のリストを返す必要があります。

    [
        { userId: 1252, userName: 'AAAA' }
    ]
    
21
Shashi

これは私のために働いたソリューションです。

 var intersect = function (arr1, arr2) {
            var intersect = [];
            _.each(arr1, function (a) {
                _.each(arr2, function (b) {
                    if (compare(a, b))
                        intersect.Push(a);
                });
            });

            return intersect;
        };

 var unintersect = function (arr1, arr2) {
            var unintersect = [];
            _.each(arr1, function (a) {
                var found = false;
                _.each(arr2, function (b) {
                    if (compare(a, b)) {
                        found = true;    
                    }
                });

                if (!found) {
                    unintersect.Push(a);
                }
            });

            return unintersect;
        };

        function compare(a, b) {
            if (a.userId === b.userId)
                return true;
            else return false;
        }
1
Shashi

3つの関数inBothinFirstOnly、およびinSecondOnlyを定義して、すべて2つのリストを引数として取り、関数名から理解できるようにリストを返すことができます。メインロジックは、3つすべてが依存する共通の関数operationに配置できます。

次に、そのoperationを選択するためのいくつかの実装を示します。その下のスニペットを見つけることができます。

  • 単純な古いJavaScript forループ
  • filterおよびsome配列メソッドを使用する矢印関数
  • Setを使用した最適化されたルックアップ

単純な古いforループ

// Generic helper function that can be used for the three operations:        
function operation(list1, list2, isUnion) {
    var result = [];
    
    for (var i = 0; i < list1.length; i++) {
        var item1 = list1[i],
            found = false;
        for (var j = 0; j < list2.length && !found; j++) {
            found = item1.userId === list2[j].userId;
        }
        if (found === !!isUnion) { // isUnion is coerced to boolean
            result.Push(item1);
        }
    }
    return result;
}

// Following functions are to be used:
function inBoth(list1, list2) {
    return operation(list1, list2, true);
}

function inFirstOnly(list1, list2) {
    return operation(list1, list2);
}

function inSecondOnly(list1, list2) {
    return inFirstOnly(list2, list1);
}

// Sample data
var list1 = [
    { userId: 1234, userName: 'XYZ'  }, 
    { userId: 1235, userName: 'ABC'  }, 
    { userId: 1236, userName: 'IJKL' },
    { userId: 1237, userName: 'WXYZ' }, 
    { userId: 1238, userName: 'LMNO' }
];
var list2 = [
    { userId: 1235, userName: 'ABC'  },  
    { userId: 1236, userName: 'IJKL' },
    { userId: 1252, userName: 'AAAA' }
];
  
console.log('inBoth:', inBoth(list1, list2)); 
console.log('inFirstOnly:', inFirstOnly(list1, list2)); 
console.log('inSecondOnly:', inSecondOnly(list1, list2)); 

filterおよびsome配列メソッドを使用する矢印関数

これは、いくつかのES5およびES6機能を使用します。

// Generic helper function that can be used for the three operations:        
const operation = (list1, list2, isUnion = false) =>
    list1.filter( a => isUnion === list2.some( b => a.userId === b.userId ) );

// Following functions are to be used:
const inBoth = (list1, list2) => operation(list1, list2, true),
      inFirstOnly = operation,
      inSecondOnly = (list1, list2) => inFirstOnly(list2, list1);

// Sample data
const list1 = [
    { userId: 1234, userName: 'XYZ'  }, 
    { userId: 1235, userName: 'ABC'  }, 
    { userId: 1236, userName: 'IJKL' },
    { userId: 1237, userName: 'WXYZ' }, 
    { userId: 1238, userName: 'LMNO' }
];
const list2 = [
    { userId: 1235, userName: 'ABC'  },  
    { userId: 1236, userName: 'IJKL' },
    { userId: 1252, userName: 'AAAA' }
];
  
console.log('inBoth:', inBoth(list1, list2)); 
console.log('inFirstOnly:', inFirstOnly(list1, list2)); 
console.log('inSecondOnly:', inSecondOnly(list1, list2));

ルックアップの最適化

上記のソリューションには、ネストされたループがあるためO(n²)時間の複雑さがあります-someもループを表します。したがって、大きな配列の場合は、user-idに(一時的な)ハッシュを作成することをお勧めします。これは、フィルターコールバック関数を生成する関数の引数としてSet(ES6)を提供することでon-the-flyを使用して実行できます。その後、その関数はhasを使用して一定時間でルックアップを実行できます。

// Generic helper function that can be used for the three operations:        
const operation = (list1, list2, isUnion = false) =>
    list1.filter(
        (set => a => isUnion === set.has(a.userId))(new Set(list2.map(b => b.userId)))
    );

// Following functions are to be used:
const inBoth = (list1, list2) => operation(list1, list2, true),
      inFirstOnly = operation,
      inSecondOnly = (list1, list2) => inFirstOnly(list2, list1);

// Sample data
const list1 = [
    { userId: 1234, userName: 'XYZ'  }, 
    { userId: 1235, userName: 'ABC'  }, 
    { userId: 1236, userName: 'IJKL' },
    { userId: 1237, userName: 'WXYZ' }, 
    { userId: 1238, userName: 'LMNO' }
];
const list2 = [
    { userId: 1235, userName: 'ABC'  },  
    { userId: 1236, userName: 'IJKL' },
    { userId: 1252, userName: 'AAAA' }
];
  
console.log('inBoth:', inBoth(list1, list2)); 
console.log('inFirstOnly:', inFirstOnly(list1, list2)); 
console.log('inSecondOnly:', inSecondOnly(list1, list2));
32
trincot

短い答え:

list1.filter(a => list2.some(b => a.userId === b.userId));  
list1.filter(a => !list2.some(b => a.userId === b.userId));  
list2.filter(a => !list1.some(b => a.userId === b.userId));  

長い答え:
上記のコードは、userId値によってオブジェクトをチェックします、
複雑な比較ルールが必要な場合は、カスタムコンパレータを定義できます。

comparator = function (a, b) {
    return a.userId === b.userId && a.userName === b.userName
};  
list1.filter(a => list2.some(b => comparator(a, b)));
list1.filter(a => !list2.some(b => comparator(a, b)));
list2.filter(a => !list1.some(b => comparator(a, b)));

また、参照によってオブジェクトを比較する方法もあります
警告!同じ値を持つ2つのオブジェクトは異なると見なされます:

o1 = {"userId":1};
o2 = {"userId":2};
o1_copy = {"userId":1};
o1_ref = o1;
[o1].filter(a => [o2].includes(a)).length; // 0
[o1].filter(a => [o1_copy].includes(a)).length; // 0
[o1].filter(a => [o1_ref].includes(a)).length; // 1
5
Trizalio

lodash's_.isEqualメソッドを使用します。具体的には:

list1.reduce(function(prev, curr){
  !list2.some(function(obj){
    return _.isEqual(obj, curr)
  }) ? prev.Push(curr): false;
  return prev
}, []);

上記はA given !Bに相当します(SQL用語ではA LEFT OUTER JOIN B)。コード内でコードを移動して、必要なものを取得できます!

4
Bwaxxlo
function intersect(first, second) {
    return intersectInternal(first, second, function(e){ return e });
}

function unintersect(first, second){
    return intersectInternal(first, second, function(e){ return !e });  
}

function intersectInternal(first, second, filter) {
    var map = {};

    first.forEach(function(user) { map[user.userId] = user; });

    return second.filter(function(user){ return filter(map[user.userId]); })
}
1
Sagi

以下は、最初の質問に答えるためのアンダースコア/ロダッシュ付きの関数型プログラミングソリューションです(交差)。

list1 = [ {userId:1234,userName:'XYZ'}, 
          {userId:1235,userName:'ABC'}, 
          {userId:1236,userName:'IJKL'},
          {userId:1237,userName:'WXYZ'}, 
          {userId:1238,userName:'LMNO'}
        ];

list2 = [ {userId:1235,userName:'ABC'},  
          {userId:1236,userName:'IJKL'},
          {userId:1252,userName:'AAAA'}
        ];

_.reduce(list1, function (memo, item) {
        var same = _.findWhere(list2, item);
        if (same && _.keys(same).length === _.keys(item).length) {
            memo.Push(item);
        }
        return memo
    }, []);

他の質問に答えるためにこれを改善させます;-)

0
laruiss

JSのfilterおよびsome配列メソッドを使用するだけで実行できます。

let arr1 = list1.filter(e => {
   return !list2.some(item => item.userId === e.userId);
});

これにより、list1には存在するがlist2には存在しないアイテムが返されます。両方のリストで共通のアイテムを探している場合。ただこれをしなさい。

let arr1 = list1.filter(e => {
   return list2.some(item => item.userId === e.userId); // take the ! out and you're done
});
0
Koushik Das