जावास्क्रिप्ट में एक स्ट्रिंग के सभी घटनाओं के सूचकांकों को दूसरे में कैसे खोजें?


105

मैं एक स्ट्रिंग के सभी आवृत्तियों की स्थिति को दूसरे स्ट्रिंग में खोजने की कोशिश कर रहा हूं, केस-असंवेदनशील।

उदाहरण के लिए, दी गई स्ट्रिंग:

मैंने लेबनान में यूकुले खेलना सीखा।

और खोज स्ट्रिंग le, मैं सरणी प्राप्त करना चाहता हूं:

[2, 25, 27, 33]

दोनों तार चर होंगे - यानी, मैं उनके मूल्यों को हार्ड-कोड नहीं कर सकता।

मुझे लगा कि यह नियमित अभिव्यक्ति के लिए एक आसान काम था, लेकिन कुछ समय के लिए संघर्ष करने के बाद, जो काम करेगा, मुझे भाग्य नहीं मिला।

मैंने इस उदाहरण को पाया कि इसका उपयोग कैसे किया जाए .indexOf(), लेकिन निश्चित रूप से इसे करने के लिए अधिक संक्षिप्त तरीका होना चाहिए?

जवाबों:


165
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, जिसका उपयोग आप कर रहे हैं। जैसा कि टिप्पणियों में विरिकन ने कहा है, सामान्य मामले के लिए नियमित अभिव्यक्तियों के साथ ऐसा करने के लिए आपको विशेष रेगेक्स वर्णों से बचने की आवश्यकता होगी, जिस बिंदु पर मुझे लगता है कि रेगेक्स समाधान इसके लायक होने की तुलना में सिरदर्द का अधिक हिस्सा है।

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'));होगा जैसे कि IMHO के ज्यादा करीब होगा। मुझे यकीन नहीं है कि js में एक बिल्ट-इन regex भागने का तंत्र है।
तिरछा

new RegExp(searchStr)रास्ता होगा, और हाँ, सामान्य मामले में आपको विशेष पात्रों से बचना होगा। यह वास्तव में करने के लायक नहीं है जब तक कि आपको सामान्यता के उस स्तर की आवश्यकता न हो।
टिम डाउन

1
शानदार जवाब, और बहुत मददगार। बहुत बहुत धन्यवाद, टिम!
जंगल

1
यदि खोज स्ट्रिंग एक रिक्त स्ट्रिंग है तो आपको एक अनंत लूप मिलता है ... इसके लिए एक जांच करेगा।
हेल्पमैस्टैकऑवरफ्लो मैय्यलाइफ

2
मान लो searchStr=aaaऔर वह str=aaaaaa। फिर 4 आवृत्तियों को खोजने के बजाय आपका कोड केवल 2 मिलेगा क्योंकि आप searchStr.lengthलूप में स्काइप बना रहे हैं ।
१०

18

यहाँ regex मुफ्त संस्करण है:

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")

संपादित करें : और यदि आप [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। मैंने रेगेक्स का उपयोग करके एक समाधान के खिलाफ तुलना के लिए कुछ परीक्षण चलाए। सबसे तेज़ तरीका रेगेक्स का उपयोग करने वाला था: jsperf.com/javascript-find-all
StuR

1
सबसे तेज़ तरीका indexOf jsperf.com/find-o-substrings
Ethan Yanjia Li

@LiEthan यह केवल तभी काम करेगा जब वह फ़ंक्शन अड़चन है और हो सकता है कि इनपुट स्ट्रिंग लंबी हो।
ज्यूबिक

@ जुबिक आपका समाधान अच्छा लगता है, लेकिन बस एक छोटा सा भ्रम है। क्या होगा अगर मैं इस तरह के फ़ंक्शन को कॉल करता हूं var result = indexes('aaaa', 'aa')? अपेक्षित परिणाम होना चाहिए [0, 1, 2]या [0, 2]?
काओ मोहन क्वांग

@ CaoM firstnhQuang कोड का पहला परिणाम देख रहा है। यदि आप दूसरा चाहते हैं जो आपको लूप बनाते समय और अंदर करने की आवश्यकता है यदि आप डालते हैं 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परिणाम सरणी में धक्का दे सकते हैं ...

संपादित करें: लिंक जोड़ना

@ टीम डाउन का उत्तर RegExp.exec () से परिणाम ऑब्जेक्ट का उपयोग करता है, और मेरे सभी जावास्क्रिप्ट संसाधन इसके उपयोग पर चमकते हैं (इसके अलावा आपको मिलान स्ट्रिंग देते हैं)। इसलिए जब वह उपयोग करता है result.index, तो यह किसी प्रकार का अनाम मिलान ऑब्जेक्ट है। निष्पादन के एमडीसी विवरण में , वे वास्तव में इस वस्तु का सभ्य विवरण में वर्णन करते हैं।


हा! योगदान के लिए धन्यवाद, किसी भी मामले में - मैं इसकी सराहना करता हूं!
जंगल

9

एक लाइनर का उपयोग कर String.protype.matchAll(ES2020):

[...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()एक पंक्ति में करने के बारे में चिंतित हैं , तो मैंने इसे for...ofएक लाख पुनरावृत्तियों (आपके तार का उपयोग करके) के लिए लूप के साथ चलाया । एक लाइनर का औसत 1420ms है जबकि for...ofमेरी मशीन का औसत 1150ms है। यह एक महत्वपूर्ण अंतर नहीं है, लेकिन एक लाइनर ठीक काम करेगा यदि आप केवल मुट्ठी भर मैच कर रहे हैं।

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 का उपयोग खतरनाक है।
भरत

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

सभी उत्तरों के लिए धन्यवाद। मैं उन सभी के माध्यम से चला गया और एक ऐसे फ़ंक्शन के साथ आया, जो 'सुई' प्रतिस्थापन की प्रत्येक घटना का पहला अंतिम सूचकांक देता है। मैं इसे यहाँ पोस्ट कर रहा हूँ अगर यह किसी की मदद करेगा।

कृपया ध्यान दें, यह केवल प्रत्येक घटना की शुरुआत के लिए मूल अनुरोध के समान नहीं है। यह मेरे usecase को बेहतर बनाता है क्योंकि आपको सुई की लंबाई रखने की आवश्यकता नहीं है।

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']

यह बहुत सीधा है।
इगौरव

11
सवाल यह है कि घटनाओं के सूचकांकों को कैसे खोजा जाए , न कि घटनाओं को खुद को समझने के लिए!
लकीलोके

1
विवाद इस उत्तर से सवाल से मेल नहीं खाता, लेकिन यही मैं देख रहा था :)
AlexNikonov
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.