在JavaScript中使用Array.map删除元素

时间:2008-08-12 22:21:30

标签: javascript functional-programming data-manipulation client-scripting

我想使用map()函数过滤项目数组。这是一段代码:

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

问题是过滤掉的项目仍然在数组中使用空间,我想彻底消除它们。

有什么想法吗?

编辑:谢谢,我忘记了filter(),我想要的实际上是filter()然后是map()

EDIT2:感谢您指出map()filter()未在所有浏览器中实现,但我的特定代码并非打算在浏览器中运行。

10 个答案:

答案 0 :(得分:84)

除了过滤之外,你应该使用filter方法而不是map,除非你想改变数组中的项目。

例如

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

[编辑:当然你总是可以sourceArray.filter(...).map(...)进行过滤和变异]

答案 1 :(得分:30)

我现在回答了一段时间,我的意见发生了变化。我建议您查看我的blog post,它会扩展此主题并更好地解释它。它还在替代方案的最后给出了JSperf比较。

tl; dr是这样的: 要完成您要求的(在一个函数调用中进行过滤和映射),您应该使用Array.reduce() 。但是,更具可读性 通常更快 2 方法只是使用过滤器和映射链接一起:

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

以下是对Array.reduce()如何工作的描述,以及如何在一次迭代中使用它来完成过滤和映射。如果这太过浓缩,我强烈建议您查看上面链接的博文,这是一个更加友好的介绍,有明确的示例和进展。

你给reduce一个(通常是匿名的)函数。

匿名函数有两个参数 - 一个(就像传入map / filter / forEach的匿名函数一样)是要操作的iteratee。然而,传递给reduce的匿名函数有另一个参数,即那些函数不接受,并且将在函数调用之间传递的值,通常称为 memo

请注意,虽然Array.filter()只接受一个参数(一个函数),但Array.reduce()也接受一个重要的(尽管是可选的)第二个参数:' memo'的初始值。将作为其第一个参数传递给该匿名函数,随后可以在函数调用之间进行变异和传递。 (如果没有提供,那么第一个匿名函数调用中的“memo”将默认为第一个iteratee,并且' iteratee'参数实际上将是数组中的第二个值)

在我们的例子中,我们将传入一个空数组来启动,然后根据我们的函数选择是否将我们的iteratee注入到我们的数组中 - 这是过滤过程。

最后,我们将返回正在进行的数组'在每个匿名函数调用中,reduce将获取该返回值并将其作为参数(称为memo)传递给它的下一个函数调用。

这允许在一次迭代中发生过滤器和映射,将我们所需的迭代次数减少一半。 :)

有关更完整的说明,请参阅MDN或上面的链接。 :)

减少呼叫的基本示例:

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, [])

请注意,第一个iteratee不大于1,因此被过滤。还要注意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.

如何编写自己的reduce函数的示例(这通常有助于理解这些函数,我发现):

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

真正的实现允许访问索引之类的东西,例如,但我希望这可以帮助你获得一个简单的感觉。

答案 2 :(得分:11)

这不是地图的作用。你真的想要Array.filter。或者如果你真的想从原始列表中删除元素,那么你需要使用for循环来强制执行它。

答案 3 :(得分:3)

我将此答案放在此处,因为此页面上共享的polyfill非常糟糕

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

答案 4 :(得分:3)

但您必须注意,所有浏览器都不支持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;
    };
}

这样做,您可以为您可能需要的任何方法制作原型。

答案 5 :(得分:2)

var arr = [1,2,'xxx','yyy']

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

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

答案 6 :(得分:0)

以下语句使用map函数清除对象。

var arraytoclean = [{v:65, toberemoved:"gronf"}, {v:12, toberemoved:null}, {v:4}];
arraytoclean.map((x,i)=>x.toberemoved=undefined);
console.dir(arraytoclean);

答案 7 :(得分:0)

首先,您可以使用地图,并且可以通过链接使用过滤器

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

答案 8 :(得分:0)

TLDR:使用 map(需要时返回 undefined)和 then filter


首先,我相信 map + 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")

答案 9 :(得分:0)

我刚刚编写了可以正确处理重复项的数组交集

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' ]