ユニークなフィルタ

10460 ワード

これは私からの再投稿ですmedium account . 私は、彼らのひどい貨幣化モデルを導入したので、長い間媒体から離れて動く予定です.とにかく、それは私のより人気のあるポストの一つですので、私はあなたがそれをお楽しみください!
しばらくの間、私は単純な問題について考えてきました.filter 配列のすべての一意の値を取得するには、JavaScriptの配列です.短い答えは、することができます!しかし、私は、これをする最も良い方法を見つけるために、JavaScriptレーンを下って短い旅行に行きました.

概要


私にとって、この方法をろ過することは自然に感じます.適用の目的.filter 配列に不要な値、すなわち重複を削除することです.使用せずにこれを達成する多くの方法があります.filter しかし、これらはどちらも読みにくく、よりエレガントな感じ.使用の追加の利点.filter また、他の配列関数の中からきちんとチェーンを切ることもできます..map , .reduce , など.filter コール)
あなたが使用しないならば.filter ES 6セットのクラスで行うことができるきちんとしたトリックがあります.これは本質的に配列をSet これは唯一の値を格納し、それを配列に戻します.清潔でエレガント.
const unique = arr => [...new Set(arr)];
unique(myArray)

バージョン1


私が思い付いた最初のバージョンのtinkeringのビットの後、これはそうでした.
const unique = (elem, index, array) => {
  for (var i = 0; i < index; i++) {
    if (array[i] === elem) return false;
  }
  return true;
};

myArray.filter(unique);
試してみるrunkit .
私は、最初にこのアプローチでかなり幸せでした.
  • それは文字列と数字(プリミティブ)のために動作します.私は、深い等値比較のためにこの仕事をしようとしていませんでした.
  • 現在の要素の前にある配列要素だけを見るので、値の最初のインスタンスは戻りますtrue を返します.false . 配列全体を見る必要はありません.
  • 複製が見つかるとすぐに、それは偽を返します(配列の残りをチェックし続けません).
  • 何がより良いだろうか?


    しばらくして、私はバージョン1がかなり偉大に働いたが、それがよりよくありえたと理解しました.この例を、1000個の項目を持つ配列を考えます.
    const myArray = [<...998 items that aren't 1>, 1, 1];
    
    最後の項目に到達すると、同じ要素が既に存在するかどうかを調べるために(999アイテム)前の項目を見始めます.配列位置から始まる[0] 最後まで2番目に到達するまで、すべてのアイテムをチェックし続ける1 配列位置[998] .
    しかし、我々がすでに見つけたので、これは不必要であると感じます1 したがって、我々はその後の任意の知っている1 ’sはユニークでありえません.繰り返し値の配列全体を再チェックする必要がないように、それぞれ独自の値をキャッシュすることができれば素晴らしいでしょう.
    それから、私自身のキャッシュオブジェクトを作成することについて考え始めました、そして、それはキャッシュに値を格納するのが難しくなくて、彼らがすでに存在するかどうかチェックする機能を提供します、しかし、これはおそらく、私は車輪を再発明していますSet です.

    バージョン2


    それから、私は再び私について考えさせましたSet() そして、それは優雅さです、そして、私はどうにか、私がどうにか、これを.filter .
    つは、使用する関数を書くとの課題の一つ.フィルタは、引数のフィルターに制限されています.現在の要素、インデックス、および初期配列のみを使用できます.残念ながら、あなたが繰り返しているような結果のために構築された配列を提供しません.reduce , “アキュムレータ”.これがケースであるならば、これは解決するより簡単な問題であるでしょう.
    いくつかのハッキングの後、これは私が思い付いた.
    const unique = () => {
      let cache;
    
      return (elem, index, array) => {
        if (!cache) cache = new Set(array);
        return cache.delete(elem);
      };
    };
    
    myArray.filter(unique());
    
    試してみるrunkit .
    この解決策は、最初にSet . そして、それを繰り返すとキャッシュを使用して各値をチェックします.メソッド削除..delete キャッシュから要素を削除し、true を返します.false できないなら.
    これは、値が見つかった最初の時間ですtrue (成功した場合には)すべての要素がキャッシュに1回存在し、結果に含まれます.しかし、同じ値(すなわち、複製)のその後のチェックで、それは帰りますfalse (削除は失敗します)その値は既に削除されており、結果から除外されています.
    パフォーマンスの面では、このソリューションは素晴らしいです、我々はすべてのユニークな値を前面に見つけるので、各反復は純粋に一意の値のリストに対して単一のチェックです.繰り返し同じ値をチェックする必要はありません.
    つの警告は、このソリューションは、キャッシュを生成する必要がありますし、各呼び出し間で共有し、このため、閉鎖が必要です.filter(unique()) むしろ.filter(unique) . しかし、これはパフォーマーとクリーンな解決のための公正なトレードオフだと思います.

    インターネットの解決策


    私はこの記事を発表した後、インターネット上でかなりきちんとした解決策を発見しました.それはすべてのより美しくする1つのライナーです.
    const unique = (x, i, a) => a.indexOf(x) == i;
    
    myArray.filter(unique);
    
    それで、私は多分私が私の時間を浪費したと考え始めました?

    パフォーマンス


    私は、私が最初にこの記事を掲示したので、パフォーマンスメトリクスが変わったかもしれないと思っています
    パズルの最後の部分は、解決策は、最も演奏者だった.あなたは自分から結果を見ることができますJSBench .

    正直に言うと、彼らはすべてかなりきれいです.バージョン2は、“インターネットの解決策”よりも優れているバージョン1よりも優れています.それは、彼らはすべて非常に近いです、そして、それはたぶん私の解決(バージョン2)価値があるために非常に大きなデータセットをとるでしょう.それで、これがあなたが必要とする何かでないならば、私は多分ちょうど1つのライナーで行くでしょう.

    包む


    それで、それはたいへんそれです.あなたのプロジェクトにrunkitまたはコピーと貼り付けの私のソリューションを試してみることができます.NPMパッケージも作成しましたyouneek あなたが好むならば.あなたがこの記事/解決策が好きならば、あなたが私に与えることができる最高の感謝は、星ですgithub .

    付録


    私は深い平等を使用してオブジェクトをフィルタリングする場合はどうですか?それは私が達成しようとしているものの範囲を超えていますが、おそらく最初の解決策(バージョン1)を使用することができますfast-deep-equal を比較します.
    const isEqual = require('fast-deep-equal');
    
    const deepUnique = (elem, index, array) => {
      for (var i = 0; i < index; i++) {
        if (isEqual(array[i], elem)) return false;
      }
      return true;
    };
    
    myArray.filter(deepUnique);