javascript arrays set-intersection set-difference set-operations

javascript - Diferencia e intersección de dos matrices que contienen objetos



arrays set-intersection (7)

Tengo dos matrices list1 y list2 que tienen objetos con algunas propiedades; userId es el Id o propiedad única:

list1 = [ { userId: 1234, userName: ''XYZ'' }, { userId: 1235, userName: ''ABC'' }, { userId: 1236, userName: ''IJKL'' }, { userId: 1237, userName: ''WXYZ'' }, { userId: 1238, userName: ''LMNO'' } ] list2 = [ { userId: 1235, userName: ''ABC'' }, { userId: 1236, userName: ''IJKL'' }, { userId: 1252, userName: ''AAAA'' } ]

Estoy buscando una manera fácil de ejecutar las siguientes tres operaciones:

  1. list1 operation list2 debería devolver la intersección de elementos:

    [ { userId: 1235, userName: ''ABC'' }, { userId: 1236, userName: ''IJKL'' } ]

  2. list1 operation list2 debería devolver la lista de todos los elementos de list1 que no se producen en list2 :

    [ { userId: 1234, userName: ''XYZ'' }, { userId: 1237, userName: ''WXYZ'' }, { userId: 1238, userName: ''LMNO'' } ]

  3. list2 operation list1 debería devolver la lista de elementos de list2 que no aparecen en list1 :

    [ { userId: 1252, userName: ''AAAA'' } ]


Aquí hay una solución de programación funcional con guión bajo / lodash para responder a su primera pregunta (intersección).

list1 = [ {userId:1234,userName:''XYZ''}, {userId:1235,userName:''ABC''}, {userId:1236,userName:''IJKL''}, {userId:1237,userName:''WXYZ''}, {userId:1238,userName:''LMNO''} ]; list2 = [ {userId:1235,userName:''ABC''}, {userId:1236,userName:''IJKL''}, {userId:1252,userName:''AAAA''} ]; _.reduce(list1, function (memo, item) { var same = _.findWhere(list2, item); if (same && _.keys(same).length === _.keys(item).length) { memo.push(item); } return memo }, []);

Te dejaré mejorar esto para responder las otras preguntas ;-)


Esta es la solución que funcionó para mí.

var intersect = function (arr1, arr2) { var intersect = []; _.each(arr1, function (a) { _.each(arr2, function (b) { if (compare(a, b)) intersect.push(a); }); }); return intersect; }; var unintersect = function (arr1, arr2) { var unintersect = []; _.each(arr1, function (a) { var found = false; _.each(arr2, function (b) { if (compare(a, b)) { found = true; } }); if (!found) { unintersect.push(a); } }); return unintersect; }; function compare(a, b) { if (a.userId === b.userId) return true; else return false; }


Puede definir tres funciones inBoth , inFirstOnly e inSecondOnly que toman dos listas como argumentos y devuelven una lista como se puede entender del nombre de la función. La lógica principal se podría poner en una operation función común operation que confían los tres.

Aquí hay algunas implementaciones para elegir esa operation , para lo cual puede encontrar un fragmento más abajo:

  • JavaScript simple for bucles
  • Funciones de flecha usando filter y some métodos de matriz
  • Búsqueda optimizada con un Set

Llano viejo for bucles

// Generic helper function that can be used for the three operations: function operation(list1, list2, isUnion) { var result = []; for (var i = 0; i < list1.length; i++) { var item1 = list1[i], found = false; for (var j = 0; j < list2.length && !found; j++) { found = item1.userId === list2[j].userId; } if (found === !!isUnion) { // isUnion is coerced to boolean result.push(item1); } } return result; } // Following functions are to be used: function inBoth(list1, list2) { return operation(list1, list2, true); } function inFirstOnly(list1, list2) { return operation(list1, list2); } function inSecondOnly(list1, list2) { return inFirstOnly(list2, list1); } // Sample data var list1 = [ { userId: 1234, userName: ''XYZ'' }, { userId: 1235, userName: ''ABC'' }, { userId: 1236, userName: ''IJKL'' }, { userId: 1237, userName: ''WXYZ'' }, { userId: 1238, userName: ''LMNO'' } ]; var list2 = [ { userId: 1235, userName: ''ABC'' }, { userId: 1236, userName: ''IJKL'' }, { userId: 1252, userName: ''AAAA'' } ]; console.log(''inBoth:'', inBoth(list1, list2)); console.log(''inFirstOnly:'', inFirstOnly(list1, list2)); console.log(''inSecondOnly:'', inSecondOnly(list1, list2));

Funciones de flecha usando filter y some métodos de matriz

Utiliza algunas características de ES5 y ES6:

// Generic helper function that can be used for the three operations: const operation = (list1, list2, isUnion = false) => list1.filter( a => isUnion === list2.some( b => a.userId === b.userId ) ); // Following functions are to be used: const inBoth = (list1, list2) => operation(list1, list2, true), inFirstOnly = operation, inSecondOnly = (list1, list2) => inFirstOnly(list2, list1); // Sample data const list1 = [ { userId: 1234, userName: ''XYZ'' }, { userId: 1235, userName: ''ABC'' }, { userId: 1236, userName: ''IJKL'' }, { userId: 1237, userName: ''WXYZ'' }, { userId: 1238, userName: ''LMNO'' } ]; const list2 = [ { userId: 1235, userName: ''ABC'' }, { userId: 1236, userName: ''IJKL'' }, { userId: 1252, userName: ''AAAA'' } ]; console.log(''inBoth:'', inBoth(list1, list2)); console.log(''inFirstOnly:'', inFirstOnly(list1, list2)); console.log(''inSecondOnly:'', inSecondOnly(list1, list2));

Optimizando la búsqueda

Las soluciones anteriores tienen una complejidad de tiempo O (n²) debido al bucle anidado; some representan un bucle. Entonces, para matrices grandes, será mejor que crees un hash (temporal) en la identificación de usuario. Esto se puede hacer sobre la marcha proporcionando un Set (ES6) como argumento para una función que generará la función de devolución de llamada del filtro. Esa función puede realizar la búsqueda en tiempo constante con has :

// Generic helper function that can be used for the three operations: const operation = (list1, list2, isUnion = false) => list1.filter( (set => a => isUnion === set.has(a.userId))(new Set(list2.map(b => b.userId))) ); // Following functions are to be used: const inBoth = (list1, list2) => operation(list1, list2, true), inFirstOnly = operation, inSecondOnly = (list1, list2) => inFirstOnly(list2, list1); // Sample data const list1 = [ { userId: 1234, userName: ''XYZ'' }, { userId: 1235, userName: ''ABC'' }, { userId: 1236, userName: ''IJKL'' }, { userId: 1237, userName: ''WXYZ'' }, { userId: 1238, userName: ''LMNO'' } ]; const list2 = [ { userId: 1235, userName: ''ABC'' }, { userId: 1236, userName: ''IJKL'' }, { userId: 1252, userName: ''AAAA'' } ]; console.log(''inBoth:'', inBoth(list1, list2)); console.log(''inFirstOnly:'', inFirstOnly(list1, list2)); console.log(''inSecondOnly:'', inSecondOnly(list1, list2));


Simplemente use el filter y some métodos de matriz de JS y puede hacerlo.

let arr1 = list1.filter(e => { return !list2.some(item => item.userId === e.userId); });

Esto devolverá los elementos que están presentes en list1 pero no en list2 . Si está buscando los elementos comunes en ambas listas. Solo haz esto.

let arr1 = list1.filter(e => { return list2.some(item => item.userId === e.userId); // take the ! out and you''re done });


Use lodash''s método lodash''s _.isEqual . Específicamente:

list1.reduce(function(prev, curr){ !list2.some(function(obj){ return _.isEqual(obj, curr) }) ? prev.push(curr): false; return prev }, []);

Arriba le da el equivalente de A given !B (en términos de SQL, A LEFT OUTER JOIN B ). ¡Puede mover el código alrededor del código para obtener lo que desea!


respuesta corta:

list1.filter(a => list2.some(b => a.userId === b.userId)); list1.filter(a => !list2.some(b => a.userId === b.userId)); list2.filter(a => !list1.some(b => a.userId === b.userId));

respuesta más larga:
El código anterior verificará los objetos por valor de userId de userId ,
Si necesita reglas de comparación complejas, puede definir un comparador personalizado:

comparator = function (a, b) { return a.userId === b.userId && a.userName === b.userName }; list1.filter(a => list2.some(b => comparator(a, b))); list1.filter(a => !list2.some(b => comparator(a, b))); list2.filter(a => !list1.some(b => comparator(a, b)));

También hay una manera de comparar objetos por referencias
¡ADVERTENCIA! dos objetos con los mismos valores se considerarán diferentes:

o1 = {"userId":1}; o2 = {"userId":2}; o1_copy = {"userId":1}; o1_ref = o1; [o1].filter(a => [o2].includes(a)).length; // 0 [o1].filter(a => [o1_copy].includes(a)).length; // 0 [o1].filter(a => [o1_ref].includes(a)).length; // 1


function intersect(first, second) { return intersectInternal(first, second, function(e){ return e }); } function unintersect(first, second){ return intersectInternal(first, second, function(e){ return !e }); } function intersectInternal(first, second, filter) { var map = {}; first.forEach(function(user) { map[user.userId] = user; }); return second.filter(function(user){ return filter(map[user.userId]); }) }