112

関数を使用してアイテムの配列をフィルタリングしたいと思いmap()ます。コード スニペットを次に示します。

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

問題は、フィルターで除外されたアイテムがまだ配列内のスペースを使用しているため、それらを完全に消去したいことです。

何か案が?

編集: ありがとう、私は を忘れていfilter()ましfilter()map()

EDIT2:私の特定のコードはブラウザで実行することを意図していませんでしたが、すべてのブラウザに実装されているわけではありませんmap()filter()

4

9 に答える 9

122

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

例えば。

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

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

于 2008-08-12T22:38:28.327 に答える
51

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

つまり、**tl;dr は次のとおりです。

求めていること (1 つの関数呼び出し内でのフィルタリングとマッピング) を達成するには、Array.reduce()** を使用します。

ただし、より読みやすく (それほど重要ではありませんが)通常は大幅に高速な2のアプローチは、フィルターとマップを連結して使用することです。

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

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


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

その無名関数は 2 つのパラメーターを取ります。1 つは (map/filter/forEach に渡される無名関数のように) 操作対象の iteratee です。reduce に渡される無名関数には別の引数がありますが、それらの関数は受け入れません。これは関数呼び出し間で渡される値であり、多くの場合memoと呼ばれます。

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

この場合、最初に空の配列を渡し、関数に基づいて iteratee を配列に挿入するかどうかを選択します。これがフィルタリング プロセスです。

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

これにより、フィルターとマップを 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, [])

最初の iteratee が 1 より大きくなかったので、フィルター処理されたことに注意してください。また、その存在を明確にし、注意を引くために名前が付けられた initialMemo にも注意してください。もう一度、最初の無名関数呼び出しに 'memo' として渡され、次に無名関数の戻り値が '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;
}

実際の実装では、たとえばインデックスなどにアクセスできますが、これがその要点を簡単に理解するのに役立つことを願っています.

于 2016-04-11T19:25:52.623 に答える
11

それはマップが行うことではありません。あなたは本当に欲しいArray.filter。または、元のリストから要素を本当に削除したい場合は、for ループを使用して命令的に行う必要があります。

于 2008-08-12T22:33:45.107 に答える
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 )

于 2009-09-30T14:56:21.877 に答える
1

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

そうすることで、必要なメソッドのプロトタイプを作成できます。

于 2008-08-12T23:44:10.503 に答える
1

TLDR: (必要に応じmapて戻る) を使用し、次に.undefined filter


まず、両方で計算を繰り返したくないので、マップ + フィルター関数が役立つと思います。Swift はもともとこの関数を呼び出してflatMapいましたが、名前を に変更しましたcompactMap

たとえば、compactMap関数がない場合、computation定義済みが 2 回になる可能性があります。

  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")
于 2021-02-19T18:10:59.157 に答える