J'ai deux tableaux list1
et list2
qui ont des objets avec quelques propriétés; userId
est l'ID ou la propriété unique:
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' }
]
Je recherche un moyen simple d'exécuter les trois opérations suivantes:
list1 operation list2
devrait renvoyer l'intersection des éléments:
[
{ userId: 1235, userName: 'ABC' },
{ userId: 1236, userName: 'IJKL' }
]
list1 operation list2
devrait renvoyer la liste de tous les éléments de list1
qui ne se produisent pas dans list2
:
[
{ userId: 1234, userName: 'XYZ' },
{ userId: 1237, userName: 'WXYZ' },
{ userId: 1238, userName: 'LMNO' }
]
list2 operation list1
devrait renvoyer la liste des éléments de list2
qui ne se produisent pas dans list1
:
[
{ userId: 1252, userName: 'AAAA' }
]
C'est la solution qui a fonctionné pour moi.
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;
}
Vous pouvez définir trois fonctions inBoth
, inFirstOnly
et inSecondOnly
qui prennent toutes deux listes comme arguments et renvoient une liste telle que l’on peut comprendre à partir du nom de la fonction. La logique principale pourrait être placée dans une fonction commune operation
sur laquelle les trois s'appuient.
Voici quelques implémentations parmi lesquelles choisir operation
, pour lesquelles vous pouvez trouver un extrait plus bas:
for
filter
et some
Set
for
// 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));
filter
et some
Cela utilise certaines fonctionnalités ES5 et 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));
Les solutions ci-dessus ont une complexité temporelle O (n²) en raison de la boucle imbriquée - some
représente également une boucle. Donc, pour les grands tableaux, vous feriez mieux de créer un hachage (temporaire) sur l'ID utilisateur. Cela peut être fait à la volée en fournissant un argument Set
(ES6) à une fonction qui générera la fonction de rappel du filtre. Cette fonction peut alors effectuer la recherche en temps constant avec 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));
réponse courte:
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));
réponse plus longue:
Le code ci-dessus vérifiera les objets par userId
valeur,
si vous avez besoin de règles de comparaison complexes, vous pouvez définir un comparateur personnalisé:
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)));
Il existe également un moyen de comparer les objets par des références
ATTENTION! deux objets avec les mêmes valeurs seront considérés comme différents:
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
Utilisez lodash's_.isEqual
méthode. Plus précisément:
list1.reduce(function(prev, curr){
!list2.some(function(obj){
return _.isEqual(obj, curr)
}) ? prev.Push(curr): false;
return prev
}, []);
Ci-dessus vous donne l'équivalent de A given !B
(en termes SQL, A LEFT OUTER JOIN B
). Vous pouvez déplacer le code autour du code pour obtenir ce que vous voulez!
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]); })
}
Voici une solution programmation fonctionnelle avec soulignement/lodash pour répondre à votre première question (intersection).
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
}, []);
Je vous laisse améliorer cela pour répondre aux autres questions ;-)
Utilisez simplement les méthodes de tableau filter
et some
de JS et vous pouvez le faire.
let arr1 = list1.filter(e => {
return !list2.some(item => item.userId === e.userId);
});
Cela retournera les éléments présents dans list1
mais pas dans list2
. Si vous recherchez les éléments communs dans les deux listes. Faites ça.
let arr1 = list1.filter(e => {
return list2.some(item => item.userId === e.userId); // take the ! out and you're done
});