javascript underscore.js

javascript - utilizando el método de "diferencia" de subrayado en arreglos de objetos



underscore.js (8)

¿No entiendo por qué estas respuestas son tan complejas a menos que me falta algo?

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

_.difference([], [])

este método funciona bien cuando tengo datos de tipo primitivo como

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

y la _.difference(a,b) devuelve [1,3,4]

pero en caso de que esté usando un objeto como

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

no parece funcionar


De hecho, puedo imaginar situaciones en las que preferiría usar el enfoque @ kontr0l que otra cosa, pero debes entender que este enfoque es cuadrático, así que básicamente este código es una abstracción para el enfoque ingenuo: itera a través de todos los valores en dos matrices.

Hay enfoques mejores que cuadráticos, no usaré aquí ninguna gran notación O, pero aquí hay dos enfoques principales, ambos son mejores que uno simple:

  • iterar a través de una de las matrices y verificar la existencia en una segunda matriz ordenada utilizando la búsqueda binaria.
  • poner valores en set / hash / dictionary / lo que sea.

Como ya se ha mencionado, se puede adoptar el primer enfoque para los objetos si se indexOf método de difference estándar con el uso de indexOf método analógico más flexible del método indexOf .

Con el segundo enfoque podemos golpear la pared con el hecho de que, a partir de Feb''2015, solo los navegadores modernos son compatibles con Sets . A partir de hashes (bueno, objetos) en JavaScript, solo pueden tener claves de tipo cadena, por lo que cualquier objeto invocado como clave primero debe convertirse a través del método toString . Por lo tanto, debemos proporcionar alguna => correspondencia. En la práctica, en la mayoría de los casos es bastante sencillo, por ejemplo, para su ejemplo particular, dicha correspondencia puede ser simplemente String(obj.id) .

Teniendo tal correspondencia, también podemos usar el siguiente enfoque lodas / undercore:

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

Pero comprando una restricción un poco más estricta, que podemos construir correspondencia entre nuestros objetos establecidos y los números naturales, podemos construir algoritmos aún más eficientes, es decir, todos nuestros identificadores son enteros no negativos, podemos usar algoritmos más eficientes.

El truco es implementar el conjunto introduciendo dos matrices de ayuda de esta manera:

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

Entonces podemos introducir set con mapeo a 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; } } }

y finalmente, podemos introducir la intersección:

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

Por lo tanto, depender de la estructura de los datos que está trabajando puede ayudarlo algunas veces.


La razón es simplemente que el objeto con el mismo contenido no son los mismos objetos, por ejemplo

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

No devolverá 0 sino -1 porque estamos buscando un objeto diferente

Ver el código fuente http://underscorejs.org/underscore.js , _.difference usa _.contains

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

y _.contains última instancia utiliza el indexOf Por lo tanto, no encontrará objetos a menos que apunten al mismo objeto.

Puede mejorar el guión bajo _.contains al recorrer todos los elementos y llamar a una devolución de llamada de comparación, que debe poder pasar a la función de diferencia o contiene o puede verificar esta versión que mejora contiene métodos


Perdóneme por saltar aquí tarde, pero esto puede ayudar:

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


Si bien la respuesta aceptada es correcta y las otras respuestas también brindan buenas ideas, existe una opción adicional que es bastante fácil de implementar con guiones bajos.

Esta solución depende de que cada objeto tenga una ID única, pero en muchos casos esto será cierto, y usted puede obtener la diferencia de dos matrices de objetos en solo dos líneas de código.

Usando el método de "arranque" de subrayado, puede construir rápidamente una matriz de todas las ID en su conjunto de origen y el conjunto de destino. A partir de ahí, todos los métodos de matriz de subrayado funcionarán, diferencia, unión, intersección, etc.

Después de la operación, es trivial obtener la lista de objetos de su lista fuente que desee. Aquí hay un ejemplo:

Verboso:

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

o, más concisamente:

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

Por supuesto, esta misma técnica se puede ampliar para usar con cualquiera de los métodos de matriz.


prueba esto por tamaño para encontrar la diferencia de una matriz de objetos:

var test = [{a: 1},{b: 2}]; var test2 = [{a: 1}]; _.filter(test, function(obj){ return !_.findWhere(test2, obj); });


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


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)