J'ai donc deux tableaux:
const allLanguages = [ 'ES', 'EN', 'DE' ]
const usedLanguages = [ { id: 1, lang: 'EN' } ]
Quelle est la façon la plus rapide de produire un nouveau tableau qui fait la différence entre ces deux? En JavaScript old school, il faudrait faire une boucle for à l'intérieur d'une autre boucle for, je pense ...
Par exemple:
const availableLanguages = [ 'ES', 'DE' ]
Vous pouvez utiliser filter()
et find()
pour renvoyer un tableau filtré.
const allLanguages = [ 'ES', 'EN', 'DE' ]
const usedLanguages = [ { id: 1, lang: 'EN' } ]
var result = allLanguages.filter(e => !usedLanguages.find(a => e == a.lang));
console.log(result)
Vous pouvez également map()
deuxième tableau, puis utiliser includes()
pour filtrer les doublons.
const allLanguages = [ 'ES', 'EN', 'DE' ]
const usedLanguages = [ { id: 1, lang: 'EN' } ].map(e => e.lang);
var result = allLanguages.filter(e => !usedLanguages.includes(e));
console.log(result)
Inspiré par l'excellente réponse de @Ori Drori, voici une pure solution basée sur un ensemble.
const all = new Set(allLanguages);
const used = new Set(usedLanguages.map(({lang}) => lang));
const availableLanguages = setDifference(all, used);
où
const setDifference = (a, b) => new Set([...a].filter(x => !b.has(x)));
availableLanguages
sera un ensemble, donc pour travailler avec lui en tant que tableau, vous devrez faire Array.from
ou [...map]
dessus.
Si l'on voulait que tout soit fonctionnel, alors
const not = fn => x => !fn(x);
const isIn = set => x => set.has(x);
Maintenant écris
const setDifference = (a, b) => new Set([...a].filter(not(isIn(b))));
que certains pourraient considérer comme plus sémantique ou lisible.
Cependant, ces solutions sont quelque peu insatisfaisantes et pourraient être sous-optimales. Même si Set#has
Est O(1)
, comparé à O(n)
pour find
ou some
, les performances globales sont toujours O(n)
, car nous devons parcourir tous les éléments de a
. Il serait préférable de supprimer les éléments de b
de a
, comme cela a été suggéré dans une autre réponse. Ce serait
const setDifference = (a, b) => {
const result = new Set(a);
b.forEach(x => result.delete(x));
return result;
}
Nous ne pouvons pas utiliser reduce
car cela n'est pas disponible sur les ensembles, et nous ne voulons pas avoir à convertir l'ensemble en tableau pour l'utiliser. Mais nous pouvons utiliser forEach
, qui est disponible sur les ensembles. Cette alternative serait préférable si a
est plus grand et b
est plus petit.
Très probablement, une future version de JS aura cette fonction intégrée, vous permettant de dire simplement
const availableLanguages = all.difference(used)
Enfin, si nous souhaitons explorer plus de fonctionnalités ES6, nous pourrions l'écrire comme un générateur qui génère des valeurs non dupliquées, comme dans
function* difference(array, excludes) {
for (let x of array)
if (!excludes.includes(x)) yield x;
}
Maintenant on peut écrire
console.log([...difference(allLanguages, usedLanguages)]);
Cette solution pourrait être recommandée s'il y avait une longue liste de langues, peut-être venant une par une, et que vous vouliez obtenir un flux de celles non utilisées.
Si l'on voulait O(1)
rechercher dans la liste des exclusions sans utiliser d'ensembles, l'approche classique est de pré-calculer un dictionnaire:
const dict = Object.assign({},
...usedLanguages.map(({lang}) => ({[lang]: true})));
const availableLanguages = allLanguages.filter(lang => lang in dict);
Si cette façon de calculer le dictionnaire est trop mystérieuse pour vous, alors certaines personnes utilisent reduce
:
const dict = usedLanguages.reduce((obj, {lang}) => {
obj[lang] = true;
return obj;
}, {});
Nina aime écrire ceci en utilisant l'opérateur virgule comme
const dict = usedLanguages.reduce((obj, {lang}) => (obj[lang] = true, obj), {});
ce qui permet d'économiser quelques accolades.
Ou, puisque JS a encore for
boucles :-):
const dict = {};
for (x of usedLanguages) {
dict[x.lang] = true;
}
Hé, c'est toi qui a dit que tu voulais utiliser ES6.
Vous pouvez utiliser le code suivant:
availableLanguages = allLanguages.filter((lang1) => !usedLanguages.some((lang2) => lang2.lang === lang1))
La fonction some
est une fonction moins connue que find
qui convient mieux aux cas où vous souhaitez vérifier si une condition est remplie par au moins un élément du tableau.
Itérer le usedLanguages
en utilisant Array#reduce
, Avec new Set(allLanguages)
comme valeur de départ. Supprimez la langue de used
Définissez à chaque itération. Répartissez le résultat dans le tableau. La complexité est O (n + m), où n est la longueur du tableau usedLanguages
et m la longueur de allLanguages
:
const allLanguages = [ 'ES', 'EN', 'DE' ];
const usedLanguages = [{ id: 1, lang: 'EN' }];
const result = [...usedLanguages.reduce((r, { lang }) => {
r.delete(lang);
return r;
}, new Set(allLanguages))];
console.log(result);