Удаление элементов с помощью Array.map в JavaScript
Я хотел бы отфильтровать массив элементов с помощью map()
функция. Вот фрагмент кода:
var filteredItems = items.map(function(item)
{
if( ...some condition... )
{
return item;
}
});
Проблема в том, что отфильтрованные элементы все еще используют пространство в массиве, и я хотел бы полностью стереть их.
Любая идея?
РЕДАКТИРОВАТЬ: Спасибо, я забыл о filter()
что я хотел на самом деле filter()
затем map()
,
EDIT2: Спасибо за указание, что map()
а также filter()
не реализованы во всех браузерах, хотя мой конкретный код не предназначен для запуска в браузере.
10 ответов
Вы должны использовать filter
метод, а не отображение, если вы не хотите мутировать элементы в массиве, в дополнение к фильтрации.
например.
var filteredItems = items.filter(function(item)
{
return ...some condition...;
});
[Редактировать: Конечно, вы всегда можете сделать sourceArray.filter(...).map(...)
фильтровать и мутировать]
Я написал этот ответ некоторое время назад, и мое мнение изменилось. Я рекомендую проверить мой пост в блоге, который расширяет эту тему и объясняет ее намного лучше. Это также дает сравнение JSperf в конце альтернатив.
Tl;dr заключается в следующем: чтобы выполнить то, что вы просите (фильтрация и отображение в пределах одного вызова функции), вы должны использоватьArray.reduce()
, Однако более читаемый и обычно более быстрый подход состоит в том, чтобы просто использовать фильтр и карту вместе:
[1,2,3].filter(num => num > 2).map(num => num * 2)
Далее следует описание того, как Array.reduce()
работает, и как это можно использовать для выполнения фильтра и сопоставления за одну итерацию. Если это слишком сжато, я настоятельно рекомендую посмотреть пост в блоге, связанный выше, который является гораздо более дружественным вступлением с ясными примерами и прогрессом.
Вы даете уменьшить аргумент, который является (обычно анонимной) функцией.
Эта анонимная функция принимает два параметра - один (как анонимные функции, передаваемые в map/filter/forEach) - это итератор, с которым нужно работать. Однако для передачи анонимной функции есть еще один аргумент, заключающийся в том, что эти функции не принимаются, и это значение, которое будет передаваться между вызовами функций, часто называемое памяткой.
Обратите внимание, что в то время как Array.filter() принимает только один аргумент (функцию), Array.reduce() также принимает важный (хотя и необязательный) второй аргумент: начальное значение для memo, которое будет передано этой анонимной функции в качестве ее первый аргумент, а затем может быть видоизменен и передан между вызовами функций. (Если он не указан, то по умолчанию "memo" в первом вызове анонимной функции будет первым итератором, а аргумент "итератора" фактически будет вторым значением в массиве)
В нашем случае мы передадим пустой массив для запуска, а затем выберем, вставлять нашего итератора в наш массив или нет на основе нашей функции - это процесс фильтрации.
Наконец, мы будем возвращать наш "массив в процессе" при каждом вызове анонимной функции, а Reduce примет это возвращаемое значение и передаст его в качестве аргумента (называемого memo) следующему вызову функции.
Это позволяет фильтровать и отображать карту за одну итерацию, сокращая количество необходимых итераций вдвое.:)
Для более полного объяснения обратитесь к MDN или по ссылке выше.:)
Базовый пример вызова Reduce:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
// if condition is our filter
if (iteratee > 1) {
// what happens inside the filter is the map
memo.push(iteratee * 2);
}
// this return value will be passed in as the 'memo' argument
// to the next call of this function, and this function will have
// every element passed into it at some point.
return memo;
}, initialMemo)
console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]
более краткая версия:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
Обратите внимание, что первый итерируемый был не больше единицы и поэтому был отфильтрован. Также обратите внимание на initialMemo, названный просто, чтобы прояснить его существование и привлечь к нему внимание. Еще раз, он передается как "памятка" первому вызову анонимной функции, а затем возвращаемое значение анонимной функции передается как аргумент "записки" следующей функции.
Другим примером классического варианта использования memo является возврат наименьшего или наибольшего числа в массиве. Пример:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.
Пример того, как написать свою собственную функцию приведения (это часто помогает понять функции, подобные этим, я нахожу):
test_arr = [];
// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
// if we did not pass in a second argument, then our first memo value
// will be whatever is in index zero. (Otherwise, it will
// be that second argument.)
const initialMemoIsIndexZero = arguments.length < 2;
// here we use that logic to set the memo value accordingly.
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
// here we use that same boolean to decide whether the first
// value we pass in as iteratee is either the first or second
// element
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
// memo is either the argument passed in above, or the
// first item in the list. initialIteratee is either the
// first item in the list, or the second item in the list.
memo = reduceFunc(memo, this[i]);
}
// after we've compressed the array into a single value,
// we return it.
return memo;
}
Реальная реализация позволяет получить доступ к таким вещам, как, например, индекс, но я надеюсь, что это поможет вам легко понять суть.
Это не то, что делает карта. Вы действительно хотите Array.filter. Или, если вы действительно хотите удалить элементы из исходного списка, вам необходимо сделать это с помощью цикла for.
var arr = [1,2,'xxx','yyy']
arr = arr.filter(function(e){ return e != 'xxx' });
arr // [1, 2, "yyy"]
Вы должны отметить, однако, что Array.filter
не поддерживается во всех браузерах, поэтому вы должны создать прототип:
//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
if (!Array.prototype.filter)
{
Array.prototype.filter = function(fun /*, thisp*/)
{
var len = this.length;
if (typeof fun != "function")
throw new TypeError();
var res = new Array();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
{
var val = this[i]; // in case fun mutates this
if (fun.call(thisp, val, i, this))
res.push(val);
}
}
return res;
};
}
И, делая это, вы можете создать прототип любого метода, который вам может понадобиться.
Я размещаю этот ответ здесь, потому что полифиллы, размещенные на этой странице, ужасны
function reduce(f, y, xs, context) {
var acc = y;
for (var i = 0, len = xs.length; i < len; i++)
acc = f.call(context, acc, xs[i], i, xs);
return acc;
}
function reduce1(f, xs, context) {
if (xs.length === 0)
throw Error('cannot reduce empty array without initial value');
else
return reduce(f, xs[0], xs.slice(1), context);
}
function map(f, xs, context) {
return reduce(function(acc, x, i) {
return acc.concat([
f.call(context, x, i, xs)
]);
}, [], xs);
}
function filter(f, xs, context) {
return reduce(function(acc, x, i) {
if (f.call(context, x, i, xs))
return acc.concat([x]);
else
return acc;
}, [], xs);
}
продлить прототипы
if (Array.prototype.reduce === undefined) {
Array.prototype.reduce = function(f, initialValue, context) {
if (initialValue === undefined)
return reduce1(f, this, context);
else
return reduce(f, initialValue, this, context);
};
}
if (Array.prototype.map === undefined) {
Array.prototype.map = function(f, context) {
return map(f, this, context);
};
}
if (Array.prototype.filter === undefined) {
Array.prototype.filter = function(f, context) {
return filter(f, this, context);
};
}
TL; DR: использование
map
(возвращается при необходимости), а затем
filter
.
Во-первых, я считаю, что функция карта + фильтр полезна, поскольку вы не хотите повторять вычисления в обоих. Swift изначально называл эту функцию
flatMap
но потом переименовал его в.
Например, если у нас нет функции, мы можем получить
computation
определяется дважды:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.filter(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
return isIncluded;
})
.map(x => {
let computation = x / 2 + 1;
return `${x} is included because ${computation} is even`
})
// Output: [2 is included because 2 is even, 6 is included because 4 is even]
Таким образом было бы полезно уменьшить повторяющийся код.
Действительно простой способ сделать что-то похожее на
compactMap
заключается в:
- Карта по реальным значениям или.
- Отфильтровать все
undefined
значения.
Это, конечно, зависит от того, что вам никогда не нужно будет возвращать неопределенные значения как часть исходной функции карты.
Пример:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.map(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
if (isIncluded) {
return `${x} is included because ${computation} is even`
} else {
return undefined
}
})
.filter(x => typeof x !== "undefined")
Следующий оператор очищает объект с помощью функции карты.
var arraytoclean = [{v:65, toberemoved:"gronf"}, {v:12, toberemoved:null}, {v:4}];
arraytoclean.map((x,i)=>x.toberemoved=undefined);
console.dir(arraytoclean);
Я просто написал пересечение массивов, которое правильно обрабатывает также дубликаты
https://gist.github.com/gkucmierz/8ee04544fa842411f7553ef66ac2fcf0
// array intersection that correctly handles also duplicates
const intersection = (a1, a2) => {
const cnt = new Map();
a2.map(el => cnt[el] = el in cnt ? cnt[el] + 1 : 1);
return a1.filter(el => el in cnt && 0 < cnt[el]--);
};
const l = console.log;
l(intersection('1234'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('12344'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('1234'.split``, '33456'.split``)); // [ '3', '4' ]
l(intersection('12334'.split``, '33456'.split``)); // [ '3', '3', '4' ]
Сначала вы можете использовать карту, а с цепочкой вы можете использовать фильтр
state.map(item => {
if(item.id === action.item.id){
return {
id : action.item.id,
name : item.name,
price: item.price,
quantity : item.quantity-1
}
}else{
return item;
}
}).filter(item => {
if(item.quantity <= 0){
return false;
}else{
return true;
}
});