Удаление элементов с помощью Array.map в JavaScript - PullRequest
65 голосов
/ 13 августа 2008

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

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

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

Есть идеи?

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

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

Ответы [ 6 ]

84 голосов
/ 13 августа 2008

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

например.

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

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

30 голосов
/ 11 апреля 2016

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

TL; DR это: Чтобы выполнить то, что вы просите (фильтрация и отображение в пределах одного вызова функции), вы должны использовать Array.reduce(). Тем не менее, более читабельный и обычно быстрее 2 - это просто использовать фильтр и карту, соединенные вместе:

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

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

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

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

Обратите внимание, что в то время как 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;
}

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

11 голосов
/ 13 августа 2008

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

3 голосов
/ 23 сентября 2016

Я размещаю этот ответ здесь, потому что общие заполнения на этой странице ужасны

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);
  };
}
3 голосов
/ 13 августа 2008

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

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

2 голосов
/ 30 сентября 2009
var arr = [1,2,'xxx','yyy']

arr = arr.filter(function(e){ return e != 'xxx' });

arr  // [1, 2, "yyy"]
...