JavaScriptで別の文字列に出現するすべての文字列のインデックスを見つける方法は?


104

私は、大文字と小文字を区別しない、別の文字列内の文字列のすべての出現の位置を見つけようとしています。

たとえば、次の文字列が与えられたとします。

レバノンでウクレレを弾くことを学びました。

そして検索文字列le、私は配列を取得したい:

[2, 25, 27, 33]

両方の文字列は変数になります-つまり、それらの値をハードコードすることはできません。

これは正規表現にとって簡単な作業であると考えましたが、うまくいくものを見つけるのにしばらく苦労した後、私は運がありませんでした。

を使用してこれを達成する方法のこの例を見つけました.indexOf()が、確かにそれを行うにはより簡潔な方法が必要ですか?

回答:


164
var str = "I learned to play the Ukulele in Lebanon."
var regex = /le/gi, result, indices = [];
while ( (result = regex.exec(str)) ) {
    indices.push(result.index);
}

更新

元の質問で、検索文字列を変数にする必要があることに気付きませんでした。このケースを処理するindexOfためにを使用する別のバージョンを作成しました。これで、最初のバージョンに戻ります。コメントでWrikkenによって指摘されているように、正規表現で一般的なケースでこれを行うには、特別な正規表現文字をエスケープする必要があります。

function getIndicesOf(searchStr, str, caseSensitive) {
    var searchStrLen = searchStr.length;
    if (searchStrLen == 0) {
        return [];
    }
    var startIndex = 0, index, indices = [];
    if (!caseSensitive) {
        str = str.toLowerCase();
        searchStr = searchStr.toLowerCase();
    }
    while ((index = str.indexOf(searchStr, startIndex)) > -1) {
        indices.push(index);
        startIndex = index + searchStrLen;
    }
    return indices;
}

var indices = getIndicesOf("le", "I learned to play the Ukulele in Lebanon.");

document.getElementById("output").innerHTML = indices + "";
<div id="output"></div>


2
leここで変数文字列はどのようになりますか?使用した場合でもnew Regexp(str);、特殊文字の危険性を探し、潜んされる$2.50例えば。のようなものregex = new Regexp(dynamicstring.replace(/([\\.+*?\\[^\\]$(){}=!<>|:])/g, '\\$1'));は、より近い私見です。jsに正規表現のエスケープメカニズムが組み込まれているかどうかはわかりません。
Wrikken、2010

new RegExp(searchStr)そうです、そうです、一般的なケースでは、特殊文字をエスケープする必要があります。そのレベルの一般性が必要でない限り、実際に行う価値はありません。
Tim Down

1
素晴らしい答えで、とても役に立ちました。どうもありがとう、ティム!
Bungle

1
検索文字列が空の文字列である場合、無限ループが発生します...それをチェックします。
HelpMeStackOverflowMyOnlyHope 2016

2
と仮定searchStr=aaastr=aaaaaaます。次にsearchStr.length、ループ内でスキップしているため、4つのオカレンスを見つける代わりに、コードは2つだけを見つけます。
blazs

18

ここに正規表現の無料版があります:

function indexes(source, find) {
  if (!source) {
    return [];
  }
  // if find is empty string return all indexes.
  if (!find) {
    // or shorter arrow function:
    // return source.split('').map((_,i) => i);
    return source.split('').map(function(_, i) { return i; });
  }
  var result = [];
  for (i = 0; i < source.length; ++i) {
    // If you want to search case insensitive use 
    // if (source.substring(i, i + find.length).toLowerCase() == find) {
    if (source.substring(i, i + find.length) == find) {
      result.push(i);
    }
  }
  return result;
}

indexes("I learned to play the Ukulele in Lebanon.", "le")

編集: 'aaaa'や 'aa'などの文字列に一致させて[0、2]を検索する場合は、次のバージョンを使用します。

function indexes(source, find) {
  if (!source) {
    return [];
  }
  if (!find) {
      return source.split('').map(function(_, i) { return i; });
  }
  var result = [];
  var i = 0;
  while(i < source.length) {
    if (source.substring(i, i + find.length) == find) {
      result.push(i);
      i += find.length;
    } else {
      i++;
    }
  }
  return result;
}

7
+1。Regexを使用したソリューションと比較するためにいくつかのテストを実行しました。最速の方法は、Regexを使用した方法でした:jsperf.com/javascript-find-all
StuR

1
最速の方法は、indexOf jsperf.com/find-o-substrings
Ethan Yanjia Li

@LiEthanは、その関数がボトルネックであり、入力文字列が長い場合にのみ問題になります。
jcubic

@jcubicあなたのソリューションは良いようですが、ちょっと混乱しています。このような関数を呼び出すとどうなりvar result = indexes('aaaa', 'aa')ますか?期待される結果は、[0, 1, 2]またはである必要がありますか[0, 2]
曹操Mạnhクアン

@CaoMạnhQuangはコードの最初の結果を調べています。2番目が必要な場合は、whileループを作成しi+=find.length;、それ以外の場合は内部に作成する必要がありますi++
jcubic

15

あなたは確かにこれを行うことができます!

//make a regular expression out of your needle
var needle = 'le'
var re = new RegExp(needle,'gi');
var haystack = 'I learned to play the Ukulele';

var results = new Array();//this is the results you want
while (re.exec(haystack)){
  results.push(re.lastIndex);
}

編集:RegExpのスペルを学ぶ

また、私はこれではない実現正確として、何をしたいlastIndexあなたは、プッシュすることができ-私たちに針ではない始まりの終わりを告げるが、それは近いですre.lastIndex-needle.length、結果配列に...

編集:リンクを追加する

@Tim Downの答えはRegExp.exec()からの結果オブジェクトを使用しており、すべてのJavascriptリソースは(一致した文字列を提供することを除いて)その使用についてはわかりません。したがって、彼がを使用する場合result.index、それはある種の名前のない一致オブジェクトです。の中にexecのMDCの説明、彼らは実際にまともな詳細にこのオブジェクトを記述する。


ハ!とにかく貢献してくれてありがとう-ありがとうございます!
バングル

8

String.protype.matchAll(ES2020)を使用する1つのライナー:

[...sourceStr.matchAll(new RegExp(searchStr, 'gi'))].map(a => a.index)

あなたの値を使用する:

const sourceStr = 'I learned to play the Ukulele in Lebanon.';
const searchStr = 'le';
const indexes = [...sourceStr.matchAll(new RegExp(searchStr, 'gi'))].map(a => a.index);
console.log(indexes); // [2, 25, 27, 33]

スプレッドとmap()を1行で行うのが心配な場合は、for...ofループを使用して100万回の繰り返し(文字列を使用)を実行しました。ワンライナーは平均で1420msfor...of私のマシンで平均して1150msであるのです。これは重要な違いではありませんが、一握りの一致だけを実行している場合は、1つのライナーで問題なく動作します。

matchAllカニユーズで見る


3

すべてのマッチの位置を見つけたいだけの場合は、少しハックをさせてください:

var haystack = 'I learned to play the Ukulele in Lebanon.',
    needle = 'le',
    splitOnFound = haystack.split(needle).map(function (culm)
    {
        return this.pos += culm.length + needle.length
    }, {pos: -needle.length}).slice(0, -1); // {pos: ...} – Object wich is used as this

console.log(splitOnFound);

可変長のRegExpがある場合は適切ではないかもしれませんが、役立つ場合もあります。

これは大文字と小文字が区別されます。大文字と小文字を区別しない場合は、String.toLowerCase以前に関数を使用します。


RegExpの使用は危険なので、あなたの答えが一番良いと思います。
Bharata

1

ここに簡単なコードがあります

function getIndexOfSubStr(str, searchToken, preIndex, output){
		 var result = str.match(searchToken);
     if(result){
     output.push(result.index +preIndex);
     str=str.substring(result.index+searchToken.length);
     getIndexOfSubStr(str, searchToken, preIndex, output)
     }
     return output;
  };

var str = "my name is 'xyz' and my school name is 'xyz' and my area name is 'xyz' ";
var  searchToken ="my";
var preIndex = 0;

console.log(getIndexOfSubStr(str, searchToken, preIndex, []));


0

@jcubicの答えに従ってください、彼の解決策は私のケースに小さな混乱を引き起こしました
たとえば、var result = indexes('aaaa', 'aa')それは[0, 1, 2]代わりに戻ります[0, 2]
それで私は私のケースに一致するように彼の解決策を少し更新しました

function indexes(text, subText, caseSensitive) {
    var _source = text;
    var _find = subText;
    if (caseSensitive != true) {
        _source = _source.toLowerCase();
        _find = _find.toLowerCase();
    }
    var result = [];
    for (var i = 0; i < _source.length;) {
        if (_source.substring(i, i + _find.length) == _find) {
            result.push(i);
            i += _find.length;  // found a subText, skip to next position
        } else {
            i += 1;
        }
    }
    return result;
}

0

すべての返信をありがとう。私はそれらすべてを調べて、「needle」サブストリングの各オカレンスの最初の最後のインデックスを与える関数を思いつきました。それが誰かを助けるために私はそれをここに投稿しています。

各発生の開始のみに対する元の要求とは異なることに注意してください。針の長さを保つ必要がないので、私のユースケースにより適しています。

function findRegexIndices(text, needle, caseSensitive){
  var needleLen = needle.length,
    reg = new RegExp(needle, caseSensitive ? 'gi' : 'g'),
    indices = [],
    result;

  while ( (result = reg.exec(text)) ) {
    indices.push([result.index, result.index + needleLen]);
  }
  return indices
}

0

同じ文字列も見つけることができるこの解決策をチェックしてください、何かが足りないか正しくないかどうか私に知らせてください。

function indexes(source, find) {
    if (!source) {
      return [];
    }
    if (!find) {
        return source.split('').map(function(_, i) { return i; });
    }
    source = source.toLowerCase();
    find = find.toLowerCase();
    var result = [];
    var i = 0;
    while(i < source.length) {
      if (source.substring(i, i + find.length) == find)
        result.push(i++);
      else
        i++
    }
    return result;
  }
  console.log(indexes('aaaaaaaa', 'aaaaaa'))
  console.log(indexes('aeeaaaaadjfhfnaaaaadjddjaa', 'aaaa'))
  console.log(indexes('wordgoodwordgoodgoodbestword', 'wordgood'))
  console.log(indexes('I learned to play the Ukulele in Lebanon.', 'le'))


-1
function countInString(searchFor,searchIn){

 var results=0;
 var a=searchIn.indexOf(searchFor)

 while(a!=-1){
   searchIn=searchIn.slice(a*1+searchFor.length);
   results++;
   a=searchIn.indexOf(searchFor);
 }

return results;

}

これは、正規表現ではなく、別の文字列内の文字列の出現を探します。

-1

以下のコードはあなたのために仕事をします:

function indexes(source, find) {
  var result = [];
  for(i=0;i<str.length; ++i) {
    // If you want to search case insensitive use 
    // if (source.substring(i, i + find.length).toLowerCase() == find) {
    if (source.substring(i, i + find.length) == find) {
      result.push(i);
    }
  }
  return result;
}

indexes("hello, how are you", "ar")

-2

String.prototype.matchを使用します

MDNドキュメント自体の例を次に示します。

var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
var regexp = /[A-E]/gi;
var matches_array = str.match(regexp);

console.log(matches_array);
// ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']

これは非常に簡単です。
igaurav 2017年

11
問題は、発生自体ではなく、発生のインデックスをどのように見つけるかです!
Luckylooke 2017年

1
この回答は質問に一致しませんが、それが私が探していたものです:)
AlexNikonov
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.