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

Removing elements with Array.map in JavaScript

提问人:Vincent Robert 提问时间:8/13/2008 最后编辑:TylerHVincent Robert 更新时间:8/24/2023 访问量:191914

问:

我想使用该函数过滤项目数组。下面是一个代码片段:map()

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

问题是过滤掉的项目仍然使用数组中的空间,我想完全清除它们。

我的特定代码不打算在浏览器中运行,因此我不担心浏览器对任何一个函数的支持。

有什么想法吗?

JavaScript 函数式编程 数据操作 客户端

评论

0赞 Vincent Robert 6/9/2016
您能详细说明为什么 2 次迭代比 1 次更糟糕吗?我的意思是,2*O(n) 对我来说相当于 O(2*n)......
0赞 Senseful 2/20/2021
在一个函数中筛选和映射通常很有用,不是出于性能原因,而是为了减少重复代码。事实上,Swift 内置了它的功能。有关详细信息,请参阅此答案compactMap

答:

13赞 Patrick 8/13/2008 #1

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

133赞 olliej 8/13/2008 #2

除非除了筛选之外,还想改变数组中的项,否则应使用该方法而不是 map。filter

例如。

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

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

评论

3赞 Mulan 9/24/2016
map不会发生变异
17赞 Crazywako 5/8/2017
但是你可以在 .map
1赞 alexOtano 9/28/2019
注意这一点:当你用 map 改变某些东西时,当 JS 传递引用时,它会改变对象,但正如 MDN 一样,maps 会返回突变的数组。
1赞 Kyle Baker 5/16/2020
@alexOtano 否,map 不会发生变异,也不会返回已变异的数组。它返回一个新数组。例如,x=[1,2,3];y = x.map(z => z*2);console.log(x,y);
1赞 trainoasis 1/5/2021
但是你能用 .map 改变一些并删除一些吗?
1赞 ggasp 8/13/2008 #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;
    };
}

这样一来,你就可以对你可能需要的任何方法进行原型设计。

评论

2赞 Kyle Baker 4/26/2018
如果你真的打算使用这个方法,请使用适当的polyfill,或者更好的是像Modernizr这样的库。否则,您可能会遇到令人困惑的错误和晦涩难懂的浏览器,直到它们在生产环境中运行太久后,您才会意识到这些错误。
7赞 vsync 9/30/2009 #4

Array Filter 方法

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 )

评论

1赞 jack blank 12/2/2015
你也可以做var arr = [1,2,"xxx", "yyy"]; arr = arr.filter(function(e){ return e!="xxx" }) console.log(arr)
0赞 Mulan 6/25/2019
4年后你回来添加大文字?减一
0赞 vsync 6/25/2019
@user633183 你指的是谁?什么“大文字”?您的评论不清楚。你确定你在正确的地方发表评论吗......?
65赞 Kyle Baker 4/12/2016 #5

受到写这个答案的启发,我后来扩展并写了一篇博客文章,仔细详细地讨论了这个问题。如果你想更深入地理解如何思考这个问题,我建议你去看看——我试着一点一点地解释它,并在最后给出一个JSperf比较,讨论速度方面的考虑。

也就是说,**tl;博士是这样的:

若要完成您要求的操作(在一个函数调用中进行筛选和映射),请使用 **。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”将默认为第一个迭代对象,而“iteratee”参数实际上是数组中的第二个值)

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

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

这允许在一次迭代中进行过滤和映射,将我们所需的迭代次数减少一半 - 不过,每次迭代只做两倍的工作,所以除了函数调用之外,什么都没有真正保存,这在 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, [])

请注意,第一个迭代对象不大于 1,因此被筛选。还要注意 initialMemo,命名只是为了明确它的存在并引起人们的注意。再一次,它作为“memo”传递给第一个匿名函数调用,然后匿名函数的返回值作为“memo”参数传递给下一个函数。

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

例如,真正的实现允许访问索引之类的东西,但我希望这能帮助您对它的要点有一个简单的感觉。

评论

0赞 congusbongus 4/16/2018
的另一个用处是,与 + 不同,回调可以传递一个索引参数,该参数是原始数组的索引,而不是过滤后的数组的索引。reducefiltermap
-1赞 gkucmierz 11/15/2019 #6

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

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

-1赞 Rishab Surana 4/21/2020 #7

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

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;
            }
        });
0赞 Senseful 2/20/2021 #8

TLDR:使用(需要时返回),然后 .mapundefinedfilter


首先,我相信地图+过滤函数很有用,因为您不想在两者中重复计算。Swift 最初调用了这个函数,但后来将其重命名为 。flatMapcompactMap

例如,如果我们没有函数,我们最终可能会定义两次:compactMapcomputation

  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")

评论

0赞 Kyle Baker 3/2/2021
或者你可以直接使用reduce来防止重复计算。