Удаление элементов с помощью Array.map в JavaScript

Я хотел бы отфильтровать массив элементов с помощью map() функции. Вот фрагмент кода:

var filteredItems = items.map(function(item)
{
    if( ...some condition... )
    {
        return item;
    }
});

Проблема в том, что отфильтрованные элементы по-прежнему используют пространство в массиве, и я хотел бы полностью их стереть.

Любая идея?

РЕДАКТИРОВАТЬ: Спасибо, я забыл о том filter(), что я хотел на самом деле filter() тогда map() .

EDIT2: Спасибо, что указали на это, map() и filter() не реализованы во всех браузерах, хотя мой конкретный код не предназначен для запуска в браузере.

Ответов (9)

Решение

Вам следует использовать filter метод, а не карту, если вы не хотите изменять элементы в массиве в дополнение к фильтрации.

например.

var filteredItems = items.filter(function(item)
{
    return ...some condition...;
});

[Edit: Конечно, вы всегда sourceArray.filter(...).map(...) можете фильтровать и изменять]

Вдохновленный написанием этого ответа, я позже расширил и написал сообщение в блоге, в котором подробно рассказывалось об этом. Я рекомендую проверить это, если вы хотите глубже понять, как думать об этой проблеме - я пытаюсь объяснить это по частям, а также в конце привожу сравнение JSperf, не обращая внимания на соображения скорости.

Тем не менее, ** The tl; dr это:

Чтобы выполнить то, о чем вы просите (фильтрация и сопоставление в рамках одного вызова функции), вы должны использовать Array.reduce()**.

Однако более читаемый и (что менее важно) обычно значительно более быстрый подход 2 - это просто использовать объединенные вместе фильтр и карту:

[1,2,3].filter(num => num > 2).map(num => num * 2)

Далее следует описание того, как это Array.reduce() работает, и как его можно использовать для выполнения фильтрации и сопоставления за одну итерацию. Опять же, если это слишком сжато, я настоятельно рекомендую просмотреть сообщение в блоге, указанное выше, которое является гораздо более дружелюбным вступлением с четкими примерами и прогрессом.


Вы даете reduce аргумент, который является (обычно анонимной) функцией.

Эта анонимная функция принимает два параметра - один (например, анонимные функции, переданные в map / filter / forEach) - это итерация, над которой нужно работать. Однако есть еще один аргумент для анонимной функции, переданной для уменьшения, что эти функции не принимают, и это значение, которое будет передаваться между вызовами функций, часто называемое памяткой .

Обратите внимание, что в то время как Array.filter () принимает только один аргумент (функцию), Array.reduce () также принимает важный (хотя и необязательный) второй аргумент: начальное значение для 'memo', которое будет передано этой анонимной функции в качестве ее первый аргумент и впоследствии может быть изменен и передан между вызовами функций. (Если он не указан, то «memo» в первом вызове анонимной функции по умолчанию будет первым итератором, а аргумент «итерация» будет фактически вторым значением в массиве)

В нашем случае мы передадим пустой массив для запуска, а затем выберем, вставлять ли нашего итератора в наш массив или нет, в зависимости от нашей функции - это процесс фильтрации.

Наконец, мы возвращаем наш «массив в процессе выполнения» при каждом вызове анонимной функции, а reduce примет это возвращаемое значение и передаст его в качестве аргумента (называемого памяткой) своему следующему вызову функции.

Это позволяет выполнять фильтрацию и отображение за одну итерацию, сокращая количество требуемых итераций вдвое - просто делая в два раза больше работы на каждой итерации, поэтому на самом деле ничего не сохраняется, кроме вызовов функций, которые не так дороги в javascript. .

Для более полного объяснения обратитесь к документации 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]);
        // or, more technically complete, give access to base array
        // and index to the reducer as well:
        // memo = reduceFunc(memo, this[i], i, this);
    }

    // after we've compressed the array into a single value,
    // we return it.
    return memo;
}

Реальная реализация позволяет получить доступ, например, к таким вещам, как индекс, но я надеюсь, что это поможет вам легко понять его суть.

Я просто написал пересечение массивов, которое правильно обрабатывает также дубликаты

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;
            }
        });

TL; DR: используйте map (возврат undefined при необходимости), а затем filter .


Во-первых, я считаю, что функция карта + фильтр полезна, поскольку вы не хотите повторять вычисления в обоих. Первоначально Swift вызывал эту функцию, flatMap но затем переименовал ее в compactMap .

Например, если у нас нет compactMap функции, мы можем в итоге 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 было бы полезно уменьшить повторяющийся код.

Очень простой способ сделать что-то подобное compactMap - это:

  1. Карта по реальным значениям или undefined.
  2. Отфильтруйте все 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);

Это не то, что делает карта. Вы очень хотите Array.filter. Или, если вы действительно хотите удалить элементы из исходного списка, вам нужно обязательно сделать это с помощью цикла for.

Однако вы должны отметить, что 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;
    };
}

Таким образом, вы можете создать прототип любого метода, который вам может понадобиться.

Метод фильтра массива

var arr = [1, 2, 3]

// ES5 syntax
arr = arr.filter(function(item){ return item != 3 })

// ES2015 syntax
arr = arr.filter(item => item != 3)

console.log( arr )