web-dev-qa-db-fra.com

en utilisant la méthode de "différence" du trait de soulignement sur des tableaux d'objets

_.difference([], [])

cette méthode fonctionne bien quand je vais avoir des données de type primitif comme 

var a = [1,2,3,4];
var b = [2,5,6];

et l'appel _.difference(a,b) renvoie [1,3,4]

mais dans le cas où j'utilise un objet comme 

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

ne semble pas fonctionner

36
Nas

La raison est simplement que les objets avec le même contenu ne sont pas les mêmes objets, par exemple.

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}]; 
a.indexOf({'id':1, 'value':10})

Il ne retournera pas 0 mais -1 car nous recherchons un autre objet.

Voir le code source http://underscorejs.org/underscore.js , _.difference utilise _.contains 

_.difference = function(array) {
  var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
  return _.filter(array, function(value){ return !_.contains(rest, value); });
};

et _.contains utilise finalement indexOf et ne trouvera donc pas d'objets à moins qu'ils ne pointent sur le même objet.

Vous pouvez améliorer le trait de soulignement _.contains en parcourant tous les éléments et en appelant un rappel de comparaison que vous devriez pouvoir passer à la différence ou à une fonction contient ou vous pouvez vérifier cette version qui améliore les méthodes contient

13
Anurag Uniyal

essayez ceci pour la taille afin de trouver la différence d'un tableau d'objets:

var test = [{a: 1},{b: 2}];
var test2 = [{a: 1}];

_.filter(test, function(obj){ return !_.findWhere(test2, obj); });
53
kontr0l

Bien que la réponse acceptée soit correcte et que les autres réponses donnent également de bonnes idées, il existe une option supplémentaire qui est assez facile à implémenter avec un trait de soulignement.

Cette solution repose sur le fait que chaque objet possède un identifiant unique, mais dans de nombreux cas, cela sera vrai et vous pouvez obtenir la différence entre deux tableaux d'objets en seulement deux lignes de code.

En utilisant la méthode "de pincement" du trait de soulignement, vous pouvez rapidement construire un tableau de tous les ID de votre ensemble source et de l'ensemble cible. À partir de là, toutes les méthodes de tableau de soulignement fonctionneront, différence, union, intersection, etc.

Après l'opération, il est facile d'obtenir la liste des objets de votre liste source souhaitée. Voici un exemple:

Verbeux:

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

var arr1 = _.pluck(a, "id");
var arr2 = _.pluck(b, "id");
var diff = _.difference(arr1, arr2);
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });

ou plus simplement:

var diff = _.difference(_.pluck(a, "id"), _.pluck(b, "id"));
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });

Bien entendu, cette même technique peut être étendue pour être utilisée avec n’importe laquelle des méthodes de tableau.

29
Clayton Gulick
without using underscorejs,
here is the pretty simple method i got solution ... 

a = [{'key':'123'},{'key':'222'},{'key':'333'}]
b = [{'key':'123'},{'key':'222'}]

var diff = a.filter(function(item1) {
  for (var i in b) {
    if (item1.key === b[i].key) { return false; }
  };
  return true;
});
console.log('result',diff)
3

En fait, je peux imaginer des situations dans lesquelles je préférerais utiliser l’approche @ kontr0l que autre chose, mais vous devez comprendre que cette approche est quadratique, ce code est donc une abstraction pour une approche naïve: parcourez toutes les valeurs dans deux tableaux. 

Il existe des approches meilleures que les méthodes quadratiques, je n’utiliserai pas ici la grande notation O, mais voici deux approches principales, les deux étant meilleures que naïves:

  • parcourez l'un des tableaux et vérifiez son existence dans le deuxième tableau trié à l'aide de la recherche binaire.
  • mettez les valeurs dans set/hash/dictionary/vous le nommez.

Comme il a déjà été mentionné, la première approche peut être adoptée pour les objets si vous réimplémentez la méthode standard difference en utilisant une méthode plus souple analogue de la méthode indexOf

Avec la seconde approche, nous pouvons nous heurter au fait que, à partir de février 2015, seuls les navigateurs modernes supportent Sets . En tant que hash (bien, objets) en javascript, ils ne peuvent avoir que des clés de type chaîne, ainsi tout objet appelé en tant que clé doit d'abord être converti via la méthode toString. Nous devons donc fournir des correspondances =>. En pratique, dans la plupart des cas, c'est assez simple, par exemple, pour votre exemple particulier, une telle correspondance peut être simplement String(obj.id).

Ayant une telle correspondance, nous pouvons également utiliser l’approche lodas/undercore suivante:

var idsA = _.pluck(a, 'id');
var idsB = _.pluck(b, 'id');

// actually here we can stop in some cases, because 
// quite often we need to identify object, but not the object itself - 
// for instance to send some ids through remote API.
var intersect = _.intersection(idsA, idsB);

//to be 100% sure you get the idea, here we assume that object having equal ids are treated as equal, so does not really matter which of arrays we'll iterate:

var dictA = _.object(idsA, a); // now we can find a by id faster then with _.find
var intersectObj = intersect.map(function(id) {return dictA[id})

Mais si vous admettez une restriction légèrement plus stricte - que nous pouvons établir une correspondance entre nos objets définis et des nombres naturels, nous pouvons construire un algorithme encore plus efficace, c'est-à-dire que tous nos identifiants sont des entiers non négatifs - nous pouvons utiliser un algorithme plus efficace. 

L'astuce consiste à implémenter set en introduisant deux tableaux d'assistance de cette manière:

var naturalSet = function (arr) {
    var sparse = [];
    var dense = [];

    var contains = function (i) {
        var res = sparse[i] < dense.length && dense[sparse[i]] == i;
        return res;
    }

    var add = function (v) {
        if (!contains(v)) {
            sparse[v] = dense.length;
            dense.Push(v);
        }
    }

    arr.forEach(add);

    return {
        contains: contains,
        toArray: function () {
            return dense
        },
        _getDense: function () {
            return dense
        },
        _getSparse: function () {
            return sparse
        }
    }
}

Ensuite, nous pouvons introduire un ensemble avec la cartographie de naturalSet:

var set = function (arr, valueOf) {
    var natSet = naturalSet(arr.map(valueOf));
    return {
        contains: function (item) {
            return natSet.contains(valueOf(item))
        },
        toArray: function () {
            var sparse = natSet._getSparse();
            var res = natSet._getDense().map(function (i) {
                return arr[sparse[i]];
            });
            return res;
        }
    }
}

et enfin, nous pouvons introduire l'intersection:

var intersection = function(arr1, arr2, valueOf) {
   return set(arr2.filter(set(arr1, valueOf).contains), valueOf).toArray();
}

Vous pouvez donc parfois vous aider en vous basant sur la structure des données que vous utilisez.

3
shabunc
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

var c = _.difference(a.map(e => e.id), b.map(e =>e.id));
var array = [];
array = a.map(e => {
   if(c.includes(e.id)){
     return e;
   }
}).filter(r=>r);
1
Anirudh

Ne pas comprendre pourquoi ces réponses sont si complexes à moins que quelque chose me manque?

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

// Or use lodash _.differenceBy
const difference = (array1, array2, prop = 'id') =>
  array1.filter(item1 =>
    !array2.some(item2 =>
      item2[prop] === item1[prop],
    ),
  );
  
// In one array.
console.log(difference(a, b));

// Intersection.
console.log([...difference(a, b), ...difference(b, a)]);

0
Dominic

Pardonnez-moi d’être arrivé tard ici, mais cela pourrait aider:

array_of_objects = 
    // return the non-matching items (without the expected properties)
    _.difference(array_of_objects,
        // filter original list for items with expected properties
        _.where(
            // original list
            array_of_objects,
            // expected properties
            {'id':1, 'value':10}
        )
    )
0
Gauss156