Как фильтровать массив из всех элементов другого массива
Я хотел бы понять лучший способ фильтровать массив из всех элементов другого. Я попытался с функцией фильтра, но мне не пришло, как дать ему значения, которые я хочу удалить.
Что-то вроде:
var array = [1,2,3,4];
var anotherOne = [2,4];
var filteredArray = array.filter(myCallback);
// filteredArray should now be [1,3]
function myCallBack(){
return element ! filteredArray;
//which clearly can't work since we don't have the reference <,<
}
если функция фильтра не полезна, как бы вы это реализовали?
Edit: я проверил возможный дублирующий вопрос, и это может быть полезно для тех, кто легко разбирается в javascript. Ответ, проверенный как хороший, облегчает задачу.
Ответы
Ответ 1
Вы можете использовать параметр this
функции filter()
чтобы избежать сохранения массива фильтра в глобальной переменной.
var filtered = [1, 2, 3, 4].filter(
function(e) {
return this.indexOf(e) < 0;
},
[2, 4]
);
console.log(filtered);
Ответ 2
Я бы сделал следующее:
var arr = [1,2,3,4],
brr = [2,4],
res = arr.filter(f => !brr.includes(f));
console.log(res);
Ответ 3
var array = [1,2,3,4];
var anotherOne = [2,4];
var filteredArray = array.filter(myCallBack);
function myCallBack(el){
return anotherOne.indexOf(el) < 0;
}
В обратном вызове вы проверяете, находится ли каждое значение array
в anotherOne
https://jsfiddle.net/0tsyc1sx/
Если вы используете lodash.js
, используйте _.difference
filteredArray = _.difference(array, anotherOne);
Демо
Если у вас есть массив объектов:
var array = [{id :1, name :"test1"},{id :2, name :"test2"},{id :3, name :"test3"},{id :4, name :"test4"}];
var anotherOne = [{id :2, name :"test2"}, {id :4, name :"test4"}];
var filteredArray = array.filter(function(array_el){
return anotherOne.filter(function(anotherOne_el){
return anotherOne_el.id == array_el.id;
}).length == 0
});
Демо-массив объектов
Demo diff массив объектов с lodash
Ответ 4
/* Here an example that uses (some) ES6 Javascript semantics to filter an object array by another object array. */
// x = full dataset
// y = filter dataset
let x = [
{"val": 1, "text": "a"},
{"val": 2, "text": "b"},
{"val": 3, "text": "c"},
{"val": 4, "text": "d"},
{"val": 5, "text": "e"}
],
y = [
{"val": 1, "text": "a"},
{"val": 4, "text": "d"}
];
// Use map to get a simple array of "val" values. Ex: [1,4]
let yFilter = y.map(itemY => { return itemY.val; });
// Use filter and "not" includes to filter the full dataset by the filter dataset val.
let filteredX = x.filter(itemX => !yFilter.includes(itemX.val));
// Print the result.
console.log(filteredX);
Ответ 5
На ваш вопрос есть много ответов, но я не вижу никого, кто бы использовал лямбда-выражения:
var array = [1,2,3,4];
var anotherOne = [2,4];
var filteredArray = array.filter(x => anotherOne.indexOf(x) < 0);
Ответ 6
Все вышеперечисленные решения "работают", но они менее чем оптимальны для производительности, и все они решают проблему одинаково, а именно линейный поиск всех записей в каждой точке с использованием Array.prototype.indexOf или Array.prototype.include. Гораздо более быстрое решение (в большинстве случаев даже более быстрое, чем бинарный поиск) заключалось бы в сортировке массивов и переходе к следующему шагу, как показано ниже. Однако одним недостатком является то, что для этого необходимо, чтобы все записи в массиве были числами или строками. Однако в некоторых редких случаях двоичный поиск может быть быстрее, чем прогрессивный линейный поиск. Эти случаи возникают из-за того, что мой прогрессивный линейный поиск имеет сложность O (2n 1 +n 2) (только O (n 1 +n 2) в более быстрой версии C/C++) (где n 1 равно искомый массив, а n 2 - массив фильтров), тогда как двоичный поиск имеет сложность O (n 1 ceil (log 2 n 2)) (ceil = округление до потолка) и, наконец, indexOf Поиск имеет сильно различающуюся сложность между O (n 1) и O (n 1 n 2), усредняя до O (n 1 ceil (n 2 ÷ 2)). Таким образом, indexOf будет самым быстрым в среднем только в случаях, когда (n 1, n 2) равно {1,2}, {1,3} или {x, 1 | x∈N}. Тем не менее, это все еще не идеальное представление современного оборудования. IndexOf изначально оптимизирован в максимально возможной степени в большинстве современных браузеров, что делает его очень зависимым от законов предсказания ветвлений. Таким образом, если мы сделаем такое же предположение для indexOf, как и для прогрессивного линейного и двоичного поиска - что массив предварительно отсортирован - тогда, согласно статистике, указанной в ссылке, мы можем ожидать примерно 6-кратное ускорение для IndexOf, сдвигая его сложность между O (n 1 ÷ 6) и O (n 1 n 2), усредняя к O (n 1 ceil (n 2 7 ÷ 12)). Наконец, обратите внимание, что приведенное ниже решение никогда не будет работать с объектами, потому что невозможно получить внутренний указатель объекта (для числового сравнения) в javascript. Если бы только Javascript мог быть
function sortAnyArray(a,b) { return a>b ? 1 : (a===b ? 0 : -1); }
function sortIntArray(a,b) { return (a|0) - (b|0) |0; }
const Math_clz32 = Math.clz32 || (function(log, LN2){
return function(x) {
return 31 - log(x >>> 0) / LN2 | 0; // the "| 0" acts like math.floor
};
})(Math.log, Math.LN2);
function filterArrayByAnotherArray(searchArray, filterArray) {
if (
// NOTE: This does not check the whole array. But, if you know
// that there are only strings or numbers (not a mix of
// both) in the array, then this is a safe assumption.
// Always use '==' with 'typeof' because browsers can optimize
// the '==' into '===' (ONLY IN THIS CIRCUMSTANCE)
typeof searchArray[0] == "number" &&
typeof filterArray[0] == "number" &&
(searchArray[0]|0) === searchArray[0] &&
(filterArray[0]|0) === filterArray[0]
) {filterArray
// if all entries in both arrays are integers
searchArray.sort(sortIntArray);
filterArray.sort(sortIntArray);
} else {
searchArray.sort(sortAnyArray);
filterArray.sort(sortAnyArray);
}
var searchArrayLen = searchArray.length, filterArrayLen = filterArray.length;
var progressiveLinearComplexity = ((searchArrayLen<<1) + filterArrayLen)>>>0
var binarySearchComplexity= (searchArrayLen * (32-Math_clz32(filterArrayLen-1)))>>>0;
// After computing the complexity, we can predict which algorithm will be the fastest
var i = 0;
if (progressiveLinearComplexity < binarySearchComplexity) {
// Progressive Linear Search
return searchArray.filter(function(currentValue){
while (filterArray[i] < currentValue) i=i+1|0;
// +undefined = NaN, which is always false for <, avoiding an infinite loop
return filterArray[i] !== currentValue;
});
} else {
// Binary Search
return searchArray.filter(
fastestBinarySearch(filterArray)
);
}
}
// see /questions/228361/binary-search-in-javascript/1203155#12031551 for implementation
// details about this binary search algorithim
function fastestBinarySearch(array){
var initLen = (array.length|0) - 1 |0;
const compGoto = Math_clz32(initLen) & 31;
return function(ARG_sValue) {
var sValue = ARG_sValue |0;
var len = initLen |0;
switch (compGoto) {
case 0:
if (len & 0x80000000) {
const nCB = len & 0x80000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 1:
if (len & 0x40000000) {
const nCB = len & 0xc0000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 2:
if (len & 0x20000000) {
const nCB = len & 0xe0000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 3:
if (len & 0x10000000) {
const nCB = len & 0xf0000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 4:
if (len & 0x8000000) {
const nCB = len & 0xf8000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 5:
if (len & 0x4000000) {
const nCB = len & 0xfc000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 6:
if (len & 0x2000000) {
const nCB = len & 0xfe000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 7:
if (len & 0x1000000) {
const nCB = len & 0xff000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 8:
if (len & 0x800000) {
const nCB = len & 0xff800000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 9:
if (len & 0x400000) {
const nCB = len & 0xffc00000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 10:
if (len & 0x200000) {
const nCB = len & 0xffe00000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 11:
if (len & 0x100000) {
const nCB = len & 0xfff00000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 12:
if (len & 0x80000) {
const nCB = len & 0xfff80000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 13:
if (len & 0x40000) {
const nCB = len & 0xfffc0000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 14:
if (len & 0x20000) {
const nCB = len & 0xfffe0000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 15:
if (len & 0x10000) {
const nCB = len & 0xffff0000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 16:
if (len & 0x8000) {
const nCB = len & 0xffff8000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 17:
if (len & 0x4000) {
const nCB = len & 0xffffc000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 18:
if (len & 0x2000) {
const nCB = len & 0xffffe000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 19:
if (len & 0x1000) {
const nCB = len & 0xfffff000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 20:
if (len & 0x800) {
const nCB = len & 0xfffff800;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 21:
if (len & 0x400) {
const nCB = len & 0xfffffc00;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 22:
if (len & 0x200) {
const nCB = len & 0xfffffe00;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 23:
if (len & 0x100) {
const nCB = len & 0xffffff00;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 24:
if (len & 0x80) {
const nCB = len & 0xffffff80;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 25:
if (len & 0x40) {
const nCB = len & 0xffffffc0;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 26:
if (len & 0x20) {
const nCB = len & 0xffffffe0;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 27:
if (len & 0x10) {
const nCB = len & 0xfffffff0;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 28:
if (len & 0x8) {
const nCB = len & 0xfffffff8;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 29:
if (len & 0x4) {
const nCB = len & 0xfffffffc;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 30:
if (len & 0x2) {
const nCB = len & 0xfffffffe;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 31:
if (len & 0x1) {
const nCB = len & 0xffffffff;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
}
// MODIFICATION: Instead of returning the index, this binary search
// instead returns whether something was found or not.
if (array[len|0] !== sValue) {
return true; // preserve the value at this index
} else {
return false; // eliminate the value at this index
}
};
}
Пожалуйста, смотрите мой другой пост здесь для более подробной информации об используемом алгоритме двоичного поиска.
Если вам не нравится размер файла, как у меня, вы можете пожертвовать некоторой производительностью, чтобы значительно уменьшить размер файла и повысить удобство обслуживания.
function sortAnyArray(a,b) { return a>b ? 1 : (a===b ? 0 : -1); }
function sortIntArray(a,b) { return (a|0) - (b|0) |0; }
function filterArrayByAnotherArray(searchArray, filterArray) {
if (
// NOTE: This does not check the whole array. But, if you know
// that there are only strings or numbers (not a mix of
// both) in the array, then this is a safe assumption.
typeof searchArray[0] == "number" &&
typeof filterArray[0] == "number" &&
(searchArray[0]|0) === searchArray[0] &&
(filterArray[0]|0) === filterArray[0]
) {
// if all entries in both arrays are integers
searchArray.sort(sortIntArray);
filterArray.sort(sortIntArray);
} else {
searchArray.sort(sortAnyArray);
filterArray.sort(sortAnyArray);
}
// Progressive Linear Search
var i = 0;
return searchArray.filter(function(currentValue){
while (filterArray[i] < currentValue) i=i+1|0;
// +undefined = NaN, which is always false for <, avoiding an infinite loop
return filterArray[i] !== currentValue;
});
}
Чтобы доказать разницу в скорости, давайте рассмотрим некоторые JSPerfs. Для фильтрации массива из 16 элементов бинарный поиск примерно на 17% быстрее, чем indexOf, тогда как filterArrayByAnotherArray примерно на 93% быстрее, чем indexOf. Для фильтрации массива из 256 элементов бинарный поиск примерно на 291% быстрее, чем indexOf, тогда как filterArrayByAnotherArray примерно на 353% быстрее, чем indexOf. Для фильтрации массива из 4096 элементов бинарный поиск примерно на 2655% быстрее, чем indexOf, тогда как filterArrayByAnotherArray примерно на 4627% быстрее, чем indexOf.
Ответ 7
ОА также может быть реализован в ES6 следующим образом
ES6:
const filtered = [1, 2, 3, 4].filter(e => {
return this.indexOf(e) < 0;
},[2, 4]);
Ответ 8
Лучшее описание функции filter
https://developer.mozilla.org/pl/docs/Web/JavaScript/Referencje/Obiekty/Array/filter
Вы должны просто выполнить функцию:
function conditionFun(element, index, array) {
return element >= 10;
}
filtered = [12, 5, 8, 130, 44].filter(conditionFun);
И вы не можете получить доступ к значению переменной до его назначения
Ответ 9
Вы можете настроить функцию фильтра для итерации по "фильтру".
var arr = [1, 2, 3 ,4 ,5, 6, 7];
var filter = [4, 5, 6];
var filtered = arr.filter(
function(val) {
for (var i = 0; i < filter.length; i++) {
if (val == filter[i]) {
return false;
}
}
return true;
}
);
Ответ 10
Вы можете использовать фильтр, а затем для функции фильтра используйте сокращение фильтра, которое проверяет и возвращает true, когда находит совпадение, а затем инвертирует по возврату (!). Функция фильтра вызывается один раз для каждого элемента массива. Вы не сравниваете ни один из элементов функции в своем сообщении.
var a1 = [1, 2, 3, 4],
a2 = [2, 3];
var filtered = a1.filter(function(x) {
return !a2.reduce(function(y, z) {
return x == y || x == z || y == true;
})
});
document.write(filtered);
Ответ 11
var arr1= [1,2,3,4];
var arr2=[2,4]
function fil(value){
return value !=arr2[0] && value != arr2[1]
}
document.getElementById("p").innerHTML= arr1.filter(fil)
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="p"></p>
Ответ 12
function arr(arr1,arr2){
function filt(value){
return arr2.indexOf(value) === -1;
}
return arr1.filter(filt)
}
document.getElementById("p").innerHTML = arr([1,2,3,4],[2,4])
<p id="p"></p>
Ответ 13
Более гибкий фильтрующий массив из другого массива, который содержит свойства объекта
function filterFn(array, diffArray, prop, propDiff) {
diffArray = !propDiff ? diffArray : diffArray.map(d => d[propDiff])
this.fn = f => diffArray.indexOf(f) === -1
if (prop) {
return array.map(r => r[prop]).filter(this.fn)
} else {
return array.filter(this.fn)
}
}
//You can use it like this;
var arr = [];
for (var i = 0; i < 10; i++) {
var obj = {}
obj.index = i
obj.value = Math.pow(2, i)
arr.push(obj)
}
var arr2 = [1, 2, 3, 4, 5]
var sec = [{t:2}, {t:99}, {t:256}, {t:4096}]
var log = console.log.bind(console)
var filtered = filterFn(arr, sec, 'value', 't')
var filtered2 = filterFn(arr2, sec, null, 't')
log(filtered, filtered2)
Ответ 14
Вы можете написать универсальную функцию filterByIndex() и использовать вывод типа в TS для сохранения проблем с функцией обратного вызова:
Допустим, у вас есть массив [1,2,3,4], который вы хотите фильтровать() с индексами, указанными в массиве [2,4].
var filtered = [1,2,3,4,].filter(byIndex(element => element, [2,4]))
функция byIndex ожидает функцию элемента и массив и выглядит следующим образом:
byIndex = (getter: (e:number) => number, arr: number[]) => (x: number) => {
var i = getter(x);
return arr.indexOf(i);
}
результат то
filtered = [1,3]
Ответ 15
Следующие примеры используют new Set()
для создания фильтрованного массива, который имеет только уникальные элементы:
Массив с примитивными типами данных: строка, число, логическое значение, ноль, неопределенный, символ:
const a = [1, 2, 3, 4];
const b = [3, 4, 5];
const c = Array.from(new Set(a.concat(b)));
Массив с объектами в качестве элементов:
const a = [{id:1}, {id: 2}, {id: 3}, {id: 4}];
const b = [{id: 3}, {id: 4}, {id: 5}];
const stringifyObject = o => JSON.stringify(o);
const parseString = s => JSON.parse(s);
const c = Array.from(new Set(a.concat(b).map(stringifyObject)), parseString);
Ответ 16
Решение Джека Гиффина великолепно, но не работает для массивов с числами больше 2 ^ 32. Ниже приведена скорректированная, быстрая версия для фильтрации массива на основе решения Jack, но она работает для 64-битных массивов.
const Math_clz32 = Math.clz32 || ((log, LN2) => x => 31 - log(x >>> 0) / LN2 | 0)(Math.log, Math.LN2);
const filterArrayByAnotherArray = (searchArray, filterArray) => {
searchArray.sort((a,b) => a > b);
filterArray.sort((a,b) => a > b);
let searchArrayLen = searchArray.length, filterArrayLen = filterArray.length;
let progressiveLinearComplexity = ((searchArrayLen<<1) + filterArrayLen)>>>0
let binarySearchComplexity = (searchArrayLen * (32-Math_clz32(filterArrayLen-1)))>>>0;
let i = 0;
if (progressiveLinearComplexity < binarySearchComplexity) {
return searchArray.filter(currentValue => {
while (filterArray[i] < currentValue) i=i+1|0;
return filterArray[i] !== currentValue;
});
}
else return searchArray.filter(e => binarySearch(filterArray, e) === null);
}
const binarySearch = (sortedArray, elToFind) => {
let lowIndex = 0;
let highIndex = sortedArray.length - 1;
while (lowIndex <= highIndex) {
let midIndex = Math.floor((lowIndex + highIndex) / 2);
if (sortedArray[midIndex] == elToFind) return midIndex;
else if (sortedArray[midIndex] < elToFind) lowIndex = midIndex + 1;
else highIndex = midIndex - 1;
} return null;
}
Ответ 17
Это будет самый простой способ фильтрации массива по отношению к другому массиву. Оба массива могут иметь объекты внутри, а не значения.
let array1 = [1,3,47,1,6,7];
let array2 = [3,6];
let filteredArray1 = array1.filter(el => array2.includes(el));
FilterArray1 == [3,6]\\это значения array1 после фильтра