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までお気軽にお問い合わせください。