JavaScriptでの文字列と配列のindexOfメソッドの調査
配列または文字列内の要素を見つける必要がある場合、indexOf()
はあなたの親友の1人です。
配列内のindexOf
最初にコードを記述し、後で説明します。
モジュール:findSpencer.js
const zoo = ['🐒', '🦄', '🐊', '🐸', '🐙']; const spencersIndex = zoo.indexOf('🐊'); // spencersIndex === 2 const spencer = zoo[spencersIndex]; // spencer === '🐊'
最も単純なバージョンでは、indexOf
メソッドは、検索しようとしている要素である1つの引数を取ります。 次に、el === target
を満たす配列で最初に見つかった要素のインデックスを返します。 これは、配列に2つの一致がある場合でも、indexOfは1つの結果のみを返すことを意味します。 結果は、配列内で最初に出現します(配列を左から右に読み取ります)。
配列内のどの項目もel === target
チェックを満たさない場合、-1
の値が返されます。
しかし、Skyler(Spencerの妹)も探しているとしましょう。 次に、オプションの引数を追加して、別のインデックスから検索を開始できます。
モジュール:findSkyler.js
const zoo = ['🐒', '🦄', '🐊', '🐸', '🐙', '🐊']; // Skyler just arrived! const spencersIndex = zoo.indexOf('🐊'); // === 2 same as before // We start searching after const skylersIndex = zoo.indexOf('🐊', spencersIndex + 1); // skylersIndex === 5
indexOfに基づいてすべてのインデックスを返すArrayプロトタイプにメソッドを作成することもできます。
モジュール:indexesOf.js
// Try to think of ways to make indicesOf more performant Array.prototype.indicesOf = function (target) { const result = []; let currentIndex = 0; while(true) { // here this === the array on which we call `indicesOf` const targetIndex = this.indexOf(target, currentIndex); if (targetIndex == -1) break; result.push(targetIndex); currentIndex = targetIndex +1; } return result; } const zoo = ['🐒', '🦄', '🐊', '🐸', '🐙', '🐊']; // Skyler just arrived! const alligatorsIndices = zoo.indicesOf('🐊'); // alligatorsIndices returns [2, 5]
indexOfで何が見つかりませんか?
厳密な等式比較であるトリプル等式比較el === target
を使用して、検索を中断していることに気付いたかもしれません。 これは、たとえば、参照以外では配列、オブジェクト、または関数をテストできないことを意味します。
const simpleArray = [1, 2, 3]; const simpleFunction = () => {console.log('hey')}; const simpleObject = {alligator: 'cage'}; const compositeArray = [simpleArray, simpleFunction, simpleObject]; // These all work as expected because we compare by reference compositeArray.indexOf(simpleArray); // returns 0 compositeArray.indexOf(simpleFunction); // returns 1 compositeArray.indexOf(simpleObject); // returns 2 // These won't work compositeArray.indexOf([1, 2, 3]); // returns -1 compositeArray.indexOf(() => {console.log('hey')}); // returns -1 compositeArray.indexOf({alligator: 'cage'}) // returns -1
深いindexOf
オブジェクト、配列、関数も再帰的にチェックするユーティリティを作成するとします。
モジュール:inDepthIndexOf.js
Array.prototype.deepIndexOf = function (target) { // If the target is an object, array or a function, we give it a special treatment if (typeof target === 'object' || typeof target === 'function') { // We stringify the target const searchTarget = target.toString() // We loop through all of the elements of the array for (let index = 0; index < this.length; index++){ const element = this[index] // We check if the element in the array is an object or a function AND if so we check if its' stringified value is equal to our target if ((typeof element === 'object' || typeof target === 'function') && element.toString() === searchTarget) { // if we have a match we interrupt the loop and return the index return index } } // if nothing matched we return -1 return -1 } return this.indexOf(target) } const simpleArray = [1, 2, 3]; const simpleFunction = () => {console.log('hey')}; const simpleObject = {alligator: 'cage'}; const compositeArray = [simpleArray, simpleFunction, simpleObject]; // These all work as expected because we compare by reference compositeArray.deepIndexOf(simpleArray); // returns 0 // ... You know the rest // These will work! compositeArray.deepIndexOf([1, 2, 3]); // returns 0 compositeArray.deepIndexOf(() => {console.log('hey')}); // returns 1 compositeArray.deepIndexOf({alligator: 'cage'}) // returns 2
このコードを改善する方法はたくさんあります。 手元に時間があれば、より正確でパフォーマンスの高いものにする方法を考えてみてください。 Twitterであなたのアイデアを読んでみたいです。
パフォーマンス
indexOf
の使用は、単にfor loop
を実行するよりもはるかに低速です。 それはindexOf
が遅いという意味ではありません。 配列が小さい場合、indexOf()とforループの違いはわかりません。 配列が非常に大きいために2つの方法の違いに気付く場合は、配列が非常に大きい理由と、検索を最適化する方法について疑問に思うはずです。 パフォーマンスベンチマークはJSPerfにあります。
var spencersIndex // This is faster than indexOf('🐊') but it is much uglier for(var index = 0; index < zoo.length; index ++) { if (zoo[index] === '🐊') spencersIndex = index } // spencers Index === 2
文字列のindexOf
Array.indexOf
からこのセクションにすべてのロジックを移植できます。 コーディングしましょう!
モジュール:whereIsSpencer.js
const whereIsSpencer = "We are all looking for Spencer the alligator. Spencer is a dear friend. Lookout here comes 🐊!" const spencersIndex = whereIsSpencer.indexOf('Spencer'); // spencersIndex === 23 // to find find the second occurence of 'Spencer', // we need to add one to the position of spencer #1 const secondSpencerIndex = whereIsSpencer.indexOf('Spencer', spencersIndex + 1); // secondSpencerIndex === 46 const alligatorIndex = whereIsSpencer.indexOf('🐊'); // alligatorIndex === 91 // Be careful the search is case sensitive! const lowerCaseSpencer = whereIsSpencer.indexOf('spencer'); // lowerCaseSpencer === -1
これで、Array.prototype.string
に対して同じindicesOf
関数を作成できます。
モジュール:indexesOfStrings.js
// This is a bit more concise than the previous indicesOf function // But it is exactly the same logic String.prototype.indicesOf = function (target) { let currentIndex = this.indexOf(target); const allIndices = [] while(currentIndex != -1) { allIndices.push(currentIndex); currentIndex = this.indexOf(target, currentIndex +1 ); } return allIndices; } const whereIsSpencer = "We are all looking for Spencer the alligator. Spencer is a dear friend. Lookout here comes 🐊!"; const spencerIndices = whereIsSpencer.indicesOf('Spencer'); // spencerIndices equals [23, 46]
この投稿を楽しんでいただけたでしょうか。 ご提案、ご質問、ご意見がございましたら、Twitterまでお気軽にお問い合わせください。