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)と同等です
Vincent Robert

回答:


105

filterフィルタリングに加えて、配列内のアイテムを変更する場合を除いて、マップではなくメソッドを使用する必要があります。

例えば。

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

[編集:もちろんsourceArray.filter(...).map(...)、フィルタリングと変更の両方をいつでも行うことができます]


3
map変化しない
ありがとう

14
ただし、で変更することはできますmap
Crazywako 2017年

これに注意してください。マップを使用して何かを変更するときにJSが参照を渡すと、オブジェクトが変更されますが、MDNが立っていると、マップは変更された配列を返します。
alexOtano

1
質問はフィルタリングの方法を尋ねていませんでした、質問は地図上で削除する方法を尋ねました
Dazzle

1
@alexOtanoいいえ、マップは変更されず、変更された配列を返しません。新しい配列を返します。例x=[1,2,3];y = x.map(z => z*2);console.log(x,y);
カイルベイカー

40

この答えを書くことに触発されて、私は後でこれを注意深く詳細に説明するブログ投稿を拡大して書くことになりました。この問題の考え方をより深く理解したい場合は、チェックすることをお勧めします。私はそれを1つずつ説明し、最後にJSperfの比較を行い、速度の考慮事項について説明します。

とは言うもののArray.reduce()tl; drは次のとおりです。要求していること(1つの関数呼び出し内でのフィルタリングとマッピング)を実現するには、を使用します

しかし、より読みやすい (あまり重要なこと)は通常かなり速く2のアプローチがあるだけで、使用フィルタとマップには、互いに連鎖:

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

以下は、どのようにArray.reduce()機能するか、および1回の反復でフィルターとマップを実行するためにどのように使用できるかについての説明です。繰り返しになりますが、これがあまりにも凝縮されている場合は、上記のリンク先のブログ投稿を参照することを強くお勧めします。これは、明確な例と進行状況を備えた、はるかにわかりやすいイントロです。


(通常は無名の)関数である引数をreduceに与えます。

その無名関数は2つのパラメーターを取ります。1つ(map / filter / forEachに渡される無名関数など)は、操作対象の反復子です。減らすために渡される無名関数には別の引数がありますが、これらの関数は受け入れません。これは、関数呼び出し間で渡される値であり、メモと呼ばれることがよくあります。

Array.filter()は1つの引数(関数)のみを取りますが、Array.reduce()は重要な(オプションですが)2番目の引数も取ります:その匿名関数にその匿名関数として渡される 'memo'の初期値最初の引数。その後、変更して関数呼び出し間で渡すことができます。(指定されていない場合、最初の無名関数呼び出しの「memo」がデフォルトで最初のiterateeになり、「iteratee」引数が実際には配列の2番目の値になります)

この例では、空の配列を渡して開始し、関数に基づいて反復子を配列に挿入するかどうかを選択します。これがフィルタリングプロセスです。

最後に、匿名関数呼び出しごとに「進行中の配列」を返し、reduceはその戻り値を受け取り、それを引数(メモと呼ばれる)として次の関数呼び出しに渡します。

これにより、フィルターとマップを1回の反復で実行でき、必要な反復回数を半分に減らすことができます。ただし、各反復で2倍の作業を行うだけなので、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」引数として次の関数に渡されます。

メモの典型的なユースケースのもう1つの例は、配列内の最小または最大の数値を返すことです。例:

[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 2017

+reduceとは異なり、コールバックには、フィルタリングされた配列のインデックスではなく、元の配列のインデックスであるインデックス引数を渡すことができるというもう1つの有用性があります。filtermap
congusbongus 2018

@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年後に巨大なテキストを追加するために戻ってきましたか?マイナス1
ありがとう

@ 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
この方法を本当にポリフィルする場合は、適切なポリフィルを使用するか、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;
            }
        });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.