Какой самый быстрый способ создать новый массив, в котором разница между этими двумя? Я думаю, что в старой школе JavaScript вам нужно было бы сделать цикл внутри другого цикла for...
Ответ 2
Установленный подход
Вдохновленный отличным ответом @Ori Drori, вот чистое решение на основе набора.
const all = new Set(allLanguages);
const used = new Set(usedLanguages.map(({lang}) => lang));
const availableLanguages = setDifference(all, used);
где
const setDifference = (a, b) => new Set([...a].filter(x => !b.has(x)));
availableLanguages
будет множеством, поэтому для работы с ним как с массивом вам нужно будет сделать Array.from
или [...map]
на нем.
Если кто-то хочет получить все функциональные функции, то
const not = fn => x => !fn(x);
const isIn = set => x => set.has(x);
Теперь напишите
const setDifference = (a, b) => new Set([...a].filter(not(isIn(b))));
которые некоторые могут считать более семантическими или читаемыми.
Однако эти решения несколько неудовлетворительны и могут быть субоптимальными. Несмотря на то, что Set#has
O(1)
, по сравнению с O(n)
для find
или some
, общая производительность по-прежнему O(n)
, так как мы должны выполнять итерацию по всем элементам a
. Было бы лучше удалить элементы из b
из a
, как было предложено в другом ответе. Это будет
const setDifference = (a, b) => {
const result = new Set(a);
b.forEach(x => result.delete(x));
return result;
}
Мы не можем использовать reduce
, так как это не доступно для наборов, и мы не хотим, чтобы оно было преобразовано в массив, чтобы использовать его. Но мы можем использовать forEach
, который доступен на множестве. Эта альтернатива была бы предпочтительнее, если a
больше, а b
меньше.
Скорее всего, какая-то будущая версия JS будет иметь этот встроенный модуль, позволяющий просто сказать
const availableLanguages = all.difference(used)
Основанный на генераторе подход
Наконец, если мы заинтересованы в изучении дополнительных функций ES6, мы могли бы написать это как генератор, который генерирует недвумерные значения, как в
function* difference(array, excludes) {
for (let x of array)
if (!excludes.includes(x)) yield x;
}
Теперь мы можем написать
console.log([...difference(allLanguages, usedLanguages)]);
Это решение может быть рекомендовано, если существует длинный список языков, возможно, один за другим, и вы хотите получить поток неиспользуемых.
Использование словаря
Если вы захотите O(1)
искать в списке исключений без использования наборов, классический подход состоит в том, чтобы предварительно вычислить словарь:
const dict = Object.assign({},
...usedLanguages.map(({lang}) => ({[lang]: true})));
const availableLanguages = allLanguages.filter(lang => lang in dict);
Если этот способ вычисления словаря слишком загадочен для вас, то некоторые люди используют reduce
:
const dict = usedLanguages.reduce((obj, {lang}) => {
obj[lang] = true;
return obj;
}, {});
Нине нравится писать это с помощью оператора запятой как
const dict = usedLanguages.reduce((obj, {lang}) => (obj[lang] = true, obj), {});
который сохраняет некоторые фигурные скобки.
Или, поскольку JS все еще имеет for
петли: -):
const dict = {};
for (x of usedLanguages) {
dict[x.lang] = true;
}
Эй, ты тот, кто сказал, что хочешь использовать ES6.