How to get the difference between two arrays of ob

2019-01-01 15:44发布

问题:

I have two result sets like this:

// Result 1
[
    { 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\" }
]

// Result 2
[
    { 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\"}
]

The final result I need is the difference between these arrays – the final result should be like this:

[{ value=\"a63a6f77-c637-454e-abf2-dfb9b543af6c\", display=\"Ryan\" }]

Is it possible to do something like this in JavaScript?

回答1:

Using only native JS, something like this will work:

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\"}]

function comparer(otherArray){
  return function(current){
    return otherArray.filter(function(other){
      return other.value == current.value && other.display == current.display
    }).length == 0;
  }
}

var onlyInA = a.filter(comparer(b));
var onlyInB = b.filter(comparer(a));

result = onlyInA.concat(onlyInB);

console.log(result);



回答2:

You could use Array.prototype.filter() in combination with Array.prototype.some().

Here is an example (assuming your arrays are stored in the variables result1 and result2):

//Find values that are in result1 but not in result2
var uniqueResultOne = result1.filter(function(obj) {
    return !result2.some(function(obj2) {
        return obj.value == obj2.value;
    });
});

//Find values that are in result2 but not in result1
var uniqueResultTwo = result2.filter(function(obj) {
    return !result1.some(function(obj2) {
        return obj.value == obj2.value;
    });
});

//Combine the two arrays of unique entries
var result = uniqueResultOne.concat(uniqueResultTwo);


回答3:

I take a slightly more general-purpose approach, although similar in ideas to the approaches of both @Cerbrus and @Kasper Moerch. I create a function that accepts a predicate to determine if two objects are equal (here we ignore the $$hashKey property, but it could be anything) and return a function which calculates the symmetric difference of two lists based on that predicate:

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

It has one minor advantage over Cerebrus\'s approach (as does Kasper Moerch\'s approach) in that it escapes early; if it finds a match, it doesn\'t bother checking the rest of the list. If I had a curry function handy, I would do this a little differently, but this works fine.

Explanation

A comment asked for a more detailed explanation for beginners. Here\'s an attempt.

We pass the following function to makeSymmDiffFunc:

function(x, y) {
    return x.value === y.value && x.display === y.display;
}

This function is how we decide that two objects are equal. Like all functions that return true or false, it can be called a \"predicate function\", but that\'s just terminology. The main point is that makeSymmDiffFunc is configured with a function that accepts two objects and returns true if we consider them equal, false if we don\'t.

Using that, makeSymmDiffFunc (read \"make symmetric difference function\") returns us a new function:

        return function(a, b) {
            return complement(pred, a, b).concat(complement(pred, b, a));
        };

This is the function we will actually use. We pass it two lists and it finds the elements in the first not in the second, then those in the second not in the first and combine these two lists.

Looking over it again, though, I could definitely have taken a cue from your code and simplified the main function quite a bit by using 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 uses the predicate and returns the elements of its first list not in its second. This is simpler than my first pass with a separate contains function.

Finally, the main function is wrapped in an immediately invoked function expression (IIFE) to keep the internal complement function out of the global scope.


Update, a few years later

Now that ES2015 has become pretty well ubiquitous, I would suggest the same technique, with a lot less boilerplate:

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


回答4:

I think the @Cerbrus solution is spot on. I have implemented the same solution but extracted the repeated code into it\'s own function (DRY).

 function filterByDifference(array1, array2, compareField) {
  var onlyInA = differenceInFirstArray(array1, array2, compareField);
  var onlyInb = differenceInFirstArray(array2, array1, compareField);
  return onlyInA.concat(onlyInb);
}

function differenceInFirstArray(array1, array2, compareField) {
  return array1.filter(function (current) {
    return array2.filter(function (current_b) {
        return current_b[compareField] === current[compareField];
      }).length == 0;
  });
}


回答5:

import differenceBy from \'lodash/differenceBy\'

const myDifferences = differenceBy(Result1, Result2, \'value\')

This will return the difference between two arrays of objects, using the key value to compare them. Note two things with the same value will not be returned, as the other keys are ignored.

This is a part of lodash.



回答6:

You can create an object with keys as the unique value corresponding for each object in array and then filter each array based on existence of the key in other\'s object. It reduces the complexity of the operation.

ES6

let 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\"}];
let 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\"}];

let valuesA = a.reduce((a,{value}) => Object.assign(a, {[value]:value}), {});
let valuesB = b.reduce((a,{value}) => Object.assign(a, {[value]:value}), {});
let result = [...a.filter(({value}) => !valuesB[value]), ...b.filter(({value}) => !valuesA[value])];
console.log(result);

ES5

var 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\"}];
var 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 valuesA = a.reduce(function(a,c){a[c.value] = c.value; return a; }, {});
var valuesB = b.reduce(function(a,c){a[c.value] = c.value; return a; }, {});
var result = a.filter(function(c){ return !valuesB[c.value]}).concat(b.filter(function(c){ return !valuesA[c.value]}));
console.log(result);



回答7:

I\'ve made a generalized diff that compare 2 objects of any kind and can run a modification handler gist.github.com/bortunac \"diff.js\" an ex of using :

old_obj={a:1,b:2,c:[1,2]}
now_obj={a:2 , c:[1,3,5],d:55}

so property a is modified, b is deleted, c modified, d is added

var handler=function(type,pointer){
console.log(type,pointer,this.old.point(pointer),\" | \",this.now.point(pointer)); 

}

now use like

df=new diff();
df.analize(now_obj,old_obj);
df.react(handler);

the console will show

mdf [\"a\"]  1 | 2 
mdf [\"c\", \"1\"]  2 | 3 
add [\"c\", \"2\"]  undefined | 5 
add [\"d\"]  undefined | 55 
del [\"b\"]  2 | undefined 


回答8:

I found this solution using filter and some.

resultFilter = (firstArray, secondArray) => {
  return firstArray.filter(firstArrayItem =>
    !secondArray.some(
      secondArrayItem => firstArrayItem._user === secondArrayItem._user
    )
  );
};



回答9:

               a.forEach(function( _obj ) {
                    b = b.filter(function(element) {
                        if (_obj.value == element.value && _obj.display ==element.display){
                            return element;
                        }
                    });
                });
                var difference = b;


回答10:

If you are willing to use external libraries, You can use _.difference in underscore.js to achieve this. _.difference returns the values from array that are not present in the other arrays.

_.difference([1,2,3,4,5][1,4,10])

==>[2,3,5]