How to use underscore's “intersection” on objects?

后端 未结 10 1127
春和景丽
春和景丽 2020-12-02 15:49
_.intersection([], [])

only works with primitive types, right?

It doesn\'t work with objects. How can I make it work with objects (maybe b

相关标签:
10条回答
  • 2020-12-02 15:56
    var a = {a:'a1',b:'b1'},
        b = {a:'a2',b:'b2',c:'c2'};
    
    _.pick(a,_.intersection(_.keys(a),_.keys(b)));
    
    // {a:'a1',b:'b1'}
    
    0 讨论(0)
  • 2020-12-02 15:57

    You can create another function based on underscore's function. You only have to change one line of code from the original function:

    _.intersectionObjects = function(array) {
        var slice = Array.prototype.slice; // added this line as a utility
        var rest = slice.call(arguments, 1);
        return _.filter(_.uniq(array), function(item) {
          return _.every(rest, function(other) {
            //return _.indexOf(other, item) >= 0;
            return _.any(other, function(element) { return _.isEqual(element, item); });
          });
        });
      };
    

    In this case you'd now be using underscore's isEqual() method instead of JavaScript's equality comparer. I tried it with your example and it worked. Here is an excerpt from underscore's documentation regarding the isEqual function:

    _.isEqual(object, other) 
    Performs an optimized deep comparison between the two objects, to determine if they should be considered equal.
    

    You can find the documentation here: http://documentcloud.github.com/underscore/#isEqual

    I put up the code on jsFiddle so you can test and confirm it: http://jsfiddle.net/luisperezphd/jrJxT/

    0 讨论(0)
  • 2020-12-02 16:03

    Here is an alternative algorithm that should be flexible and perform better. One of those improvements is that you can specify your own comparison function so in your case you can just compare the id if it's a unique identifier.

    function intersectionObjects2(a, b, areEqualFunction) {
        var results = [];
    
        for(var i = 0; i < a.length; i++) {
            var aElement = a[i];
            var existsInB = _.any(b, function(bElement) { return areEqualFunction(bElement, aElement); });
    
            if(existsInB) {
                results.push(aElement);
            }
        }
    
        return results;
    }
    
    function intersectionObjects() {
        var results = arguments[0];
        var lastArgument = arguments[arguments.length - 1];
        var arrayCount = arguments.length;
        var areEqualFunction = _.isEqual;
    
        if(typeof lastArgument === "function") {
            areEqualFunction = lastArgument;
            arrayCount--;
        }
    
        for(var i = 1; i < arrayCount ; i++) {
            var array = arguments[i];
            results = intersectionObjects2(results, array, areEqualFunction);
            if(results.length === 0) break;
        }
    
        return results;
    }
    

    You can use it like this:

    var a = [ { id: 1, name: 'jake' }, { id: 4, name: 'jenny'} ];
    var b = [ { id: 1, name: 'jake' }, { id: 9, name: 'nick'} ];
    var c = [ { id: 1, name: 'jake' }, { id: 4, name: 'jenny'}, { id: 9, name: 'nick'} ];
    
    var result = intersectionObjects(a, b, c, function(item1, item2) {
        return item1.id === item2.id;
    });
    

    Or you can leave out the function and it will use underscores _.isEqual() function, like so:

    var result = intersectionObjects(a, b, c);
    

    You can find it on jsFiddle here: http://jsfiddle.net/luisperezphd/43vksdn6/

    0 讨论(0)
  • 2020-12-02 16:09
    //nested array is in the format of [[],[],[]]
    
    function objectArrayIntersection(nestedArrays){     
    
        let intersectingItems = [];                
        let uniqArr = _.uniq(_.flatten(nestedArrays)); //intersecting items removed    
        const countOfNestedArrays = nestedArrays.length;
    
    
        for (let index = 0; index < uniqArr.length; index++) {
            let uniqItem = uniqArr[index];
            let foundCount = 0;
    
            for(var j = 0;j<countOfNestedArrays;j++){
                var i = _.indexOf(nestedArrays[j],uniqItem);
                if(i != -1)
                    foundCount ++;
            }
    
            if(foundCount ==  countOfNestedArrays){
                intersectingItems.push(uniqItem);
            }
        }
    
        return intersectingItems;
    }
    

    I tried solving it this way.

    0 讨论(0)
  • 2020-12-02 16:11

    I'd like to share my general solution for those cases.

    I added a general function to underscore, using mixin, which performs a binary 'array' operation on two collections, according to a given Hash function:

    _.mixin({
        collectionOperation: function(arr1, arr2, hash, action) {
            var iArr1 = _(arr1).indexBy(hash)
                , iArr2 = _(arr2).indexBy(hash);
            return action(_(iArr1).keys(), _(iArr2).keys()).map(function (id) {
                return iArr1[id] || iArr2[id];
            });
        }
    });
    

    Usage example:

    _([{id:1,v:'q'},{id:2,v:'p'}]).collectionOperation([{id:3,v:'pq'}], 'id', _.union )
    

    Note that 'id' may be replaced with a function.

    I believe this solution is O(n+m).

    0 讨论(0)
  • 2020-12-02 16:12

    The array methods in underscore are very powerful, you should only need a few lines to accomplish what you want to do:

    var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ];
    var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ];
    
    var result = _(a).chain().map(function(ea) {
        return _.find(b, function(eb) {return ea.id == eb.id;});
    }).compact().value();
    

    If you have large arrays you can get rid of the compact() call with one additional line:

    var result = [];
    _.each(a, function(ea) {
        var entry = _.find(b, function(eb) {return ea.id == eb.id;});
        if (entry) result.push(entry);
    });
    
    0 讨论(0)
提交回复
热议问题