42 votes

Comment utiliser l'"intersection" de l'underscore sur les objets ?

_.intersection([], [])

ne fonctionne qu'avec les types primitifs, non ?

Il ne fonctionne pas avec les objets. Comment puis-je le faire fonctionner avec des objets (peut-être en vérifiant le champ "Id") ?

var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ]
var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ]

Dans cet exemple, le résultat devrait être :

_.intersection(a, b);

[ ['id' : 1, 'name' : 'jake' } ] ;

26voto

luisperezphd Points 3220

Vous pouvez créer une autre fonction basée sur celle d'underscore. Vous ne devez modifier qu'une seule ligne de code de la fonction originale :

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

Dans ce cas, vous utiliseriez la méthode isEqual() d'underscore au lieu du comparateur d'égalité de JavaScript. Je l'ai essayé avec votre exemple et cela a fonctionné. Voici un extrait de la documentation d'underscore concernant la fonction isEqual :

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

Vous pouvez trouver la documentation ici : http://documentcloud.github.com/underscore/#isEqual

J'ai mis le code sur jsFiddle pour que vous puissiez le tester et le confirmer : http://jsfiddle.net/luisperezphd/jrJxT/

25voto

luisperezphd Points 3220

Voici un algorithme alternatif qui devrait être plus souple et plus performant. L'une de ces améliorations est que vous pouvez spécifier votre propre fonction de comparaison. Dans votre cas, vous pouvez simplement comparer l'identifiant s'il s'agit d'un identifiant unique.

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

Vous pouvez l'utiliser comme ceci :

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

Ou vous pouvez omettre la fonction et elle utilisera la fonction _.isEqual() soulignée, comme ceci :

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

Vous pouvez le trouver sur jsFiddle ici : http://jsfiddle.net/luisperezphd/43vksdn6/

5voto

Julian D. Points 4235

Les méthodes de tableau dans underscore sont très puissantes, vous ne devriez avoir besoin que de quelques lignes pour accomplir ce que vous voulez faire :

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

Si vous avez de grands tableaux, vous pouvez vous débarrasser de l'option compact() appel avec une ligne supplémentaire :

var result = [];
_.each(a, function(ea) {
    var entry = _.find(b, function(eb) {return ea.id == eb.id;});
    if (entry) result.push(entry);
});

4voto

Jacob Points 97

J'aimerais partager mon général solution pour ces cas.

J'ai ajouté une fonction générale à underscore, en utilisant mixin, qui effectue une opération de 'tableau' binaire sur deux collections, selon une fonction Hash donnée :

_.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];
        });
    }
});

Exemple d'utilisation :

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

Notez que "id peut être remplacé par une fonction.

Je pense que cette solution est O(n+m).

4voto

Raja Jaganathan Points 2053

Dans lodash 4.0.0. nous pouvons essayer comme ceci

var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'} ];
var b = [ {'id': 1, 'name': 'jake' }, {'id': 9, 'name': 'nick'} ];

_.intersectionBy(a, b, 'id');

Sortie :

[ ['id' : 1, 'name' : 'jake' } ] ;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X