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


90

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

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

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

任何的想法?

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

EDIT2:感谢您指出这map()filter()不能在所有的浏览器中实现,虽然我的特定代码的目的不是为了在浏览器中运行。


您能否详细说明为什么2次迭代比1次差?我的意思是2 * O(n)相当于我的O(2 * n)...
文森特·罗伯特

Answers:


105

filter除非您要对数组中的项进行除突变之外的其他操作,否则应使用方法而不是映射。

例如。

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

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


3
map不会变异
谢谢您

14
但是你可以变异map
Crazywako

请注意这一点:当JS使用map进行某些更改时,当JS通过引用时,它将更改对象,但按照MDN的规定,map返回已更改的数组。
alexOtano

1
问题不问如何过滤,问题问如何在地图上删除
Dazzle

1
@alexOtano不,映射不会突变,也不会返回突变数组。它返回一个新数组。例如,x=[1,2,3];y = x.map(z => z*2);console.log(x,y);
凯尔·贝克

40

受此答案启发,我后来扩展并撰写了一篇详细介绍此问题的博客文章。我建议您检查一下是否要对如何思考此问题有更深入的了解-我会尝试逐个进行解释,并在最后给出JSperf比较,并讨论速度方面的考虑。

也就是说,tl; dr是这样的:要完成您要的内容(在一个函数调用中进行过滤和映射),可以使用Array.reduce()

但是,更具可读性 (次要地位)通常明显更快的方法2是只使用链接在一起的过滤器和地图:

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

接下来是对Array.reduce()工作方式的描述,以及如何使用它在一次迭代中完成过滤和映射。同样,如果过于简洁,我强烈建议您查看上面链接的博客文章,这是一个更加友好的介绍,其中包含清晰的示例和进度。


您提供reduce参数(通常是匿名函数)。

该匿名函数具有两个参数-一个参数(例如传递给map / filter / forEach的匿名函数)是要操作的迭代器。对于传递的匿名函数,还有另一种说法是减少这些函数不接受,这是将在函数调用之间传递的值,通常称为memo

请注意,虽然Array.filter()仅接受一个参数(一个函数),但Array.reduce()也接受一个重要的(尽管可选)第二个参数:“ memo”的初始值将作为该参数传递给该匿名函数。第一个参数,然后可以在函数调用之间进行更改和传递。(如果未提供,则默认情况下,第一个匿名函数调用中的“ memo”将默认为第一个iteratee,而“ iteratee”自变量实际上将为数组中的第二个值)

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

最后,我们将在每个匿名函数调用中返回“进行中的数组”,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”传递给第一个匿名函数调用,然后将匿名函数的返回值作为“ 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;
}

真正的实现允许访问诸如索引之类的内容,但是我希望这可以帮助您获得其要点的简单感觉。


2
辉煌!多年来我一直想做这样的事情。决定尝试找出一种不错的方式,哇,自然的javascript!
jemiloii

reducefilter+不同的是,与+不同map,可以向回调函数传递一个索引参数,该参数是原始数组的索引,而不是已过滤数组的索引。
congusbongus

@KyleBaker指向您的博客文章的链接转到一个未找到的页面。您能否更新链接?谢谢!
蒂姆·菲利普

10

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


6

数组过滤方法

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
您也可以做var arr = [1,2,"xxx", "yyy"]; arr = arr.filter(function(e){ return e!="xxx" }) console.log(arr)
杰克·

您4年后回来添加大量文字吗?减一
谢谢,

@ user633183您指的是谁?什么是“大文本”?您的评论不清楚。您确定在正确的位置发表评论吗?
vsync

2

但是,您必须注意,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
如果您确实打算使用这种方法来填充,请使用适当的polyfill,或者使用像Modernizr这样的更好的库。否则,您可能会遇到难以理解的错误浏览器,这些错误浏览器在生产中已经存在了很长时间才意识到。
凯尔·贝克

0

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

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

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


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;
            }
        });
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.