views:

63

answers:

4

I'm trying to find the positions of all occurrences of a string in another string, case-insensitive.

For example, given the string:

I learned to play the Ukulele in Lebanon.

and the search string le, I want to obtain the array:

[2, 25, 27, 33]

Both strings will be variables - i.e., I can't hard-code their values.

I figured that this was an easy task for regular expressions, but after struggling for a while to find one that would work, I've had no luck.

I found this example of how to accomplish this using .indexOf(), but surely there has to be a more concise way to do it?

Thanks in advance for any help!

A: 

How about:

   'I learned to play the Ukulele in Lebanon'.split('le').length;

Edit: didn't read the question properly. You want positions not just counts....

snoopy
+2  A: 
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("I learned to play the Ukulele in Lebanon.", "le")

jcubic
+1 for a RegEx-free solution.
chryss
Thanks, jcubic - that looks like a good solution.
Bungle
A: 

You sure can do this!

//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);
}

Edit: learn to spell RegExp

Also, I realized this isn't exactly what you want, as lastIndex tells us the end of the needle not the beginning, but it's close - you could push re.lastIndex-needle.length into the results array...

Edit: adding link

@Tim Down's answer uses the results object from RegExp.exec(), and all my Javascript resources gloss over its use (apart from giving you the matched string). So when he uses result.index, that's some sort of unnamed Match Object. In the MDC description of exec, they actually describe this object in decent detail.

Ryley
Annnd @Tim Down has the winner, ignore me...
Ryley
Ha! Thanks for contributing, in any case - I appreciate it!
Bungle
+6  A: 
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);
}

UPDATE

I failed to spot in the original question that the search string needs to be a variable. I've written another version to deal with this case that uses indexOf, so you're back to where you started. As pointed out by Wrikken in the comments, to do this for the general case with regular expressions you would need to escape special regex characters, at which point I think the regex solution becomes more of a headache than it's worth.

function getIndicesOf(searchStr, str, caseSensitive) {
    var startIndex = 0, searchStrLen = searchStr.length;
    var 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;
}

getIndicesOf("le", "I learned to play the Ukulele in Lebanon.", false);
Tim Down
How would `le` be a variable string here? Even when using `new Regexp(str);` the danger of special characters is lurking, searching for `$2.50` for instance. Something like `regex = new Regexp(dynamicstring.replace(/([\\.+*?\\[^\\]$(){}=!<>|:])/g, '\\$1'));` would be more close IMHO. I'm not sure whether js has a built-in regex escaping mechanism.
Wrikken
`new RegExp(searchStr)` would be the way, and yes, in the general case you would have to escape special characters. It's not really worth doing unless you need that level of generality.
Tim Down
... ah, I see: I failed to spot in the question that the OP does need that level of generality. Rewriting...
Tim Down
......... done.
Tim Down
Great answer, and very helpful. Thanks so much, Tim!
Bungle