JavaScript 配列の重複排除は、フロントエンドの採用試験の筆記試験問題によく出てきます。例:
配列 var arr = ['a', 'b', 'c', '1', 0, 'c' があります。 , 1, ' ', 1, 0] の場合は、JavaScript を使用して重複排除関数 unqiue を実装し、unique(arr) が ['a', 'b', 'c', '1', 0, 1, ' を返すようにしてください。 ']
筆記試験の質問として、試験のポイントは 2 つあります:
1. 正解です。このテストのポイントを過小評価しないでください。JavaScript はブラウザ上で実行されることが多いため、さまざまなブラウザ環境で機能が正しいかどうかを確認するのは簡単ではありません。信じられない場合は、このブログを読み続けてください。
2. パフォーマンス。多くの場合、JavaScript 言語自体 (DOM などの拡張機能を除く、狭義の意味) がパフォーマンスに問題を引き起こすことはありませんが、残念ながら、これは試験問題であるため、面接官は依然としてパフォーマンスを試験点として取り上げます。
さらに読み進める前に、最適と思われるバージョンを実装することをお勧めします。
直感的なソリューション
配列の重複排除の場合、プログラムを作成していれば、すぐに最初のソリューションを得ることができます:
function unique(arr) {
var ret = []
for ( var i = 0; i var item = arr[i] if (ret.indexOf(item) === -1) { ret.push(item)
if (indexOf(ret, item) === -1) { ret.push(item) } } return ret}
この時点で、精度は失われています 問題ですが、パフォーマンスの点で、二重ループは面接官を不快にさせます。 最適化計画 最適化というと、多くの場合、8 人の仙人が海を渡り、100 の花が咲くようなものです。しかし、八仙は現実的ではないことが多く、百花はトコジラミを簡単に引き寄せる可能性があります。ここでは、アレイの重複排除のためのさまざまな最適化ソリューションについて 1 つずつ説明することはしません。ここでは、非常に優れた結果が得られる最も一般的に使用されるソリューションについてのみ説明します。 function unique(arr) { var ret = [] var hash = {} for (var i = 0; i < arr.length; i++) { var item = arr[i] var key = typeof(item) + item if (hash[key] !== 1) { ret.push(item) hash[key] = 1 } } return ret} 中心となるのは、indexOf を置き換えるハッシュ オブジェクトを構築することです。JavaScript では、オブジェクトのキー値は文字列のみであることに注意してください。そのため、var key = typeof(item) +項目は必須です。値 1 と文字列 '1' を区別してください。 しかし、最適化は非常に落とし穴を引き起こしやすいです。たとえば、上記の実装では、次の入力を判断することは不可能です: 1unique([ new String(1), new Number(1) ])
はい 良好なパフォーマンスと正確性を実現するためにコードの変更を続けます。しかし、多くの場合、結果は良くありません。 実需 ここまで書いたら、このブログも本題に入ります。プログラマーは皆、一般的なパフォーマンスと優れたパフォーマンスを備えたユニバーサル関数を作成するなど、心の中にいくつかの夢を持っています。このような夢は、プログラマーが優秀になるための重要な原動力ですが、制御しないと簡単に道を誤ってしまいます。 パフォーマンスの最適化に戻ります。最近では、コア システム、データベース、ネットワーク、フロントエンドなど、さまざまな最適化が行われています。これらすべての最適化では、次の質問に答える必要があります: 1. 現在利用可能なもの。どのようなシナリオで最適化を実行する必要があるか、またそのシナリオでの具体的な制限は何ですか。多くの場合、自由をもたらす制限を整理することが重要です。 2. 一体何が欲しいのですか?最適化の目的は何ですか。安定性を向上させるか、スループットを向上させるか、ユーザーの待ち時間を短縮するか。この質問が解決されるまで、最適化は無駄になります。この質問に対する正確な答えは、最適化に特定の測定可能なパラメータをもたらし、最適化に目標を持たせることができます。 3. 何を諦めることができますか?ケーキを持って食べることもできません。最適化の本質は、特定のシナリオにおける選択とトレードオフです。何かを諦めたくない場合、最適化は困難になることがよくあります。
このブログを書くのは筆記試験の問題に答えるためではありません。この筆記試験の問題は少し退屈です。このブログを書くそもそもの原動力は、最近 SeaJS のパフォーマンス チューニングを行っていることです。その要件の 1 つは次のとおりです。 ' )
var b = require('./b')
...
require('./a').fn(...)
})
上記は渡すモジュールです解析関数 文字列の場合、モジュールの依存関係配列 ['./a', './b', './a'] を取得できます。この依存関係情報には重複フィールドがある可能性があるため、重複を削除する必要があります。
この特定のシナリオについて、上記の 3 つの質問に答えてみましょう:
1. 現在利用可能なもの。入力にはいくつかの制限があり、文字列のみを考慮する必要があります。
2. 一体何が欲しいのですか?この問題は比較的単純であり、その固有のメソッドができるだけ高速であることを願っています。この指標は、Chrome デバッグ ツールの [プロファイル] パネルを使用して、指定されたテスト ページ内で固有のメソッドにかかる時間を表示することです。 5ミリ秒。
3. 何を諦めることができますか?文字列を処理するだけでよく、他の型はサポートされていません。諦めについて話すのは興味深いことがよくありますが、この問題はそれほど単純ではないので、次に話しましょう。
SeaJS での解決策
特定のシナリオを明確に分析すると、解決策は比較的単純です:
function unique(arr) {
var obj = {}
forEach(arr, function(item) ) {
obj[item] = 1
})
return key(obj)
}
上記のコードは forEach と key に依存しており、コンテキスト環境から分離できません (環境は非常に複雑です)重要)、完全なコード: util-lang.js
上記のソリューションは、コード サイズ、正確性、さまざまなブラウザーでの全体的なパフォーマンスの点で非常に優れています。
ある日、そのようなテストケースが現れるまで:
define(function(require, exports) {
var a = require('toString')
var b = require('hasOwnProperty')
.. .
})
「完璧な」ソリューション
上記のテスト ケースは
を呼び出します
unique([ 'toString', 'hasOwnProperty' ]) // [ 'toString', 'hasOwnProperty' ] を返すことが期待されます
IE にはさまざまなバグがあります。あまり有名ではありませんが実際にあるバグを次に示します。
var obj = { toString: 1, hasOwnProperty: 1 }
for (var p in obj) {
console.log(p)
}
最新のブラウザでは、上記は 2 つの値を正しく出力しますが、古い IE では出力されません。これは IE の列挙バグです: より安全な Object.keys 互換性の実装 「完璧な」解決策は次のとおりです。 hasOwnProperty ,
‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐‐‐‐‐‐‐‐ ‐‐‐‐ eString',
' 'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
DontEnumsLength = DontEnums.length;
return function (o) {
if (typeof o != "オブジェクト" && typeof o != "関数" || o === null)
throw new TypeError("非オブジェクトで呼び出された Object.keys" ); o) {
numBug) {
for (var i = 0 ; i < DontEnumsLength; i++) {
} }
結果を返します
};
})();
DontEnums に加えて配列の場合、hasOwnProperty の処理にも特別な注意を払うことができます。 **フロントエンドにとって、「正しさ」を保証するのは簡単ではありません。 **