@Cerbrusと@Kasper Moerchの両方のアプローチとアイデアは似ていますが、私はもう少し汎用的なアプローチを採用しています。述語を受け入れて2つのオブジェクトが等しいかどうかを判断する関数を作成し(ここでは$$hashKey
プロパティを無視しますが、何でもかまい ません)、その述語に基づいて2つのリストの対称差を計算する関数を返します。
a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}]
b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}]
var makeSymmDiffFunc = (function() {
var contains = function(pred, a, list) {
var idx = -1, len = list.length;
while (++idx < len) {if (pred(a, list[idx])) {return true;}}
return false;
};
var complement = function(pred, a, b) {
return a.filter(function(elem) {return !contains(pred, elem, b);});
};
return function(pred) {
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
};
}());
var myDiff = makeSymmDiffFunc(function(x, y) {
return x.value === y.value && x.display === y.display;
});
var result = myDiff(a, b); //=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}
これは、早期にエスケープするという点で、セレブルスのアプローチ(Kasper Moerchのアプローチと同様)に比べて1つの小さな利点があります。一致が見つかった場合、リストの残りをチェックする必要はありません。curry
便利な機能がある場合、これは少し違った方法で行いますが、これは問題なく機能します。
説明
コメントは初心者のためのより詳細な説明を求めました。これが試みです。
次の関数をに渡しますmakeSymmDiffFunc
。
function(x, y) {
return x.value === y.value && x.display === y.display;
}
この関数は、2つのオブジェクトが等しいと判断する方法です。true
またはを返すすべての関数と同様にfalse
、「述語関数」と呼ぶことができますが、それは単なる用語です。主なポイントは、makeSymmDiffFunc
2つのオブジェクトを受け入れtrue
、それらが等しいと見なす場合は等しくない場合に戻る関数で構成されていることfalse
です。
これを使用するとmakeSymmDiffFunc
(「対称差関数を作成」を参照)、新しい関数が返されます。
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
これが実際に使用する関数です。2つのリストを渡して、最初の要素が2番目の要素ではなく、2番目の要素が最初の要素ではない要素を見つけ、これら2つのリストを結合します。
ただし、もう一度見てみると、間違いなくコードからヒントを得て、次のコマンドを使用することでmain関数をかなり簡略化できますsome
。
var makeSymmDiffFunc = (function() {
var complement = function(pred, a, b) {
return a.filter(function(x) {
return !b.some(function(y) {return pred(x, y);});
});
};
return function(pred) {
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
};
}());
complement
述語を使用し、2番目ではなく最初のリストの要素を返します。これは、個別のcontains
関数を使用する最初のパスよりも簡単です。
最後に、メイン関数はすぐに呼び出される関数式(IIFE)でラップされ、内部complement
関数をグローバルスコープの外に保ちます。
更新、数年後
ES2015はかなり広く普及しているので、定型文を大幅に減らして、同じ手法を提案します。
const diffBy = (pred) => (a, b) => a.filter(x => !b.some(y => pred(x, y)))
const makeSymmDiffFunc = (pred) => (a, b) => diffBy(pred)(a, b).concat(diffBy(pred)(b, a))
const myDiff = makeSymmDiffFunc((x, y) => x.value === y.value && x.display === y.display)
const result = myDiff(a, b)
//=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}