Get the difference of arrays in ES6?

You can use filter() and find() to return filtered array.

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)

You could also map() second array and then use includes() to filter out duplicates.

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)

Set-based approach

Inspired by @Ori Drori's excellent answer, here is a pure set-based solution.

const all = new Set(allLanguages);
const used = new Set(usedLanguages.map(({lang}) => lang));

const availableLanguages = setDifference(all, used);

where

const setDifference = (a, b) => new Set([...a].filter(x => !b.has(x)));

availableLanguages will be a set, so to work with it as an array you'll need to do Array.from or [...map] on it.

If one wanted to get all functional, then

const not = fn => x => !fn(x);
const isIn = set => x => set.has(x);

Now write

const setDifference = (a, b) => new Set([...a].filter(not(isIn(b))));

which some might consider more semantic or readable.

However, these solutions are somewhat unsatisfying and could be suboptimal. Even though Set#has is O(1), compared to O(n) for find or some, the overall performance is still O(n), since we have to iterate through all the elements of a. It would be better to delete the elements from b from a, as was suggested in another answer. This would be

const setDifference = (a, b) => {
  const result = new Set(a);
  b.forEach(x => result.delete(x));
  return result;
}

We can't use reduce since that is not available on sets, and we don't want to have to convert the set into an array to use it. But we can use forEach, which is available on sets. This alternative would be preferable if a is larger and b is smaller.

Most likely some future version of JS will have this built-in, allowing you to just say

const availableLanguages = all.difference(used)

Generator-based approach

Finally, if we are interested in exploring more ES6 features, we could write this as a generator which generates non-duplicate values, as in

function* difference(array, excludes) {
  for (let x of array) 
    if (!excludes.includes(x)) yield x;
}

Now we can write

console.log([...difference(allLanguages, usedLanguages)]);

This solution might be recommended if there was a long list of languages, perhaps coming in one by one, and you wanted to get a stream of the non-used ones.

Using a dictionary

If one wanted O(1) lookup into the list of exclusions without using sets, the classic approach is to pre-calculate a dictionary:

const dict = Object.assign({}, 
    ...usedLanguages.map(({lang}) => ({[lang]: true})));

const availableLanguages = allLanguages.filter(lang => lang in dict);

If that way of computing the dictionary is too arcane for you, then some people use reduce:

const dict = usedLanguages.reduce((obj, {lang}) => {
  obj[lang] = true;
  return obj;
}, {});

Nina likes to write this using the comma operator as

const dict = usedLanguages.reduce((obj, {lang}) => (obj[lang] = true, obj), {});

which saves some curly braces.

Or, since JS still has for loops :-):

const dict = {};
for (x of usedLanguages) {
  dict[x.lang] = true;
}

Hey, you're the one who said you want to use ES6.


You can use the following code:

availableLanguages = allLanguages.filter((lang1) => !usedLanguages.some((lang2) => lang2.lang === lang1))

The some function is a lesser known relative to find that is better suited for cases where you want to check if a condition is met by at least one element in the array.