26

I am working on a javascript code to find the nth occurrence of a character in a string. Using the indexOf() function we are able to get the first occurrence of the character. Now the challenge is to get the nth occurrence of the character. I was able to get the second third occurrence and so on using the code given below:

function myFunction() {
  var str = "abcdefabcddesadfasddsfsd.";

  var n = str.indexOf("d");
  document.write("First occurence " +n );

  var n1 = str.indexOf("d",parseInt(n+1));
  document.write("Second occurence " +n1 );

  var n2 = str.indexOf("d",parseInt(n1+1));
  document.write("Third occurence " +n2 );

  var n3 = str.indexOf("d",parseInt(n2+1));
  document.write("Fourth occurence " +n3);

  // and so on ...
}

The result is given below

First occurence 3 
Second occurence 9 
Third occurence 10 
Fourth occurence 14 
Fifth occurence 18 
Sixth occurence 19

I would like to generalize the script so that I am able to find the nth occurrence of the character as the above code requires us to repeat the script n times. Let me know if there is a better method or alternative to do the same. It would be nice if we just give the occurrence (at run time) to get the index of that character.

The following are some of my questions:

  • How do we do it in JavaScript?
  • Does any framework provide any functionality to do the same implementation in an easier way or what are the alternate methods to implement the same in other frameworks /languages?
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
sathish kumar
  • 1,187
  • 5
  • 17
  • 21

6 Answers6

26
function nth_occurrence (string, char, nth) {
    var first_index = string.indexOf(char);
    var length_up_to_first_index = first_index + 1;

    if (nth == 1) {
        return first_index;
    } else {
        var string_after_first_occurrence = string.slice(length_up_to_first_index);
        var next_occurrence = nth_occurrence(string_after_first_occurrence, char, nth - 1);

        if (next_occurrence === -1) {
            return -1;
        } else {
            return length_up_to_first_index + next_occurrence;  
        }
    }
}

// Returns 16. The index of the third 'c' character.
nth_occurrence('aaaaacabkhjecdddchjke', 'c', 3);
// Returns -1. There is no third 'c' character.
nth_occurrence('aaaaacabkhjecdddhjke', 'c', 3);
Marten
  • 1,336
  • 10
  • 16
  • when I use the above code to display the occurrence using an alert I get the output as undefined(for any value other than the first occurrence). – sathish kumar Oct 05 '12 at 11:40
  • I fixed it. The problem was that javascript interpreters are allowed to insert ";" where they think they fit. So they put one after return because the return value was on the next line and therefore not recognized. – Marten Oct 05 '12 at 11:47
  • the return string was not in a single line so it was showing the undefined output.was my mistake.It worked thank you. – sathish kumar Oct 05 '12 at 11:50
  • @CQQL this fails where the nth occurrence doesn't exist - I'd expect it to return `-1` in this instance but it returns `[length up to previous index] -1` - will edit your answer to fix – wheresrhys Jan 07 '13 at 13:55
14

You can do it easily by implementing a function using charAt(), like this:

function nth_ocurrence(str, needle, nth) {
  for (i=0;i<str.length;i++) {
    if (str.charAt(i) == needle) {
        if (!--nth) {
           return i;    
        }
    }
  }
  return false;
}

alert( nth_ocurrence('aaaaacabkhjecdddchjke', 'c', 3)  );//alerts 16

Thanks to CQQL for let me know what OP really wanted. I updated a bit my original function to achieve the new behaviour.

Nelson
  • 49,283
  • 8
  • 68
  • 81
  • 1
    `str.charAt(i)` is the same as `str[i]` - but your code counts occurrences, it doesn't find the nth occurrence. – Wladimir Palant Oct 05 '12 at 11:23
  • Yes the name 'nth_ocurrence' is not accurate with what the function does, but it's the name the OP uses in his description of what he wants, which I think it's what my function does, so I kept that name to be in concordance with OP's naming. – Nelson Oct 05 '12 at 11:47
  • So let's summarize it: You have made a function that has the name that the OP introduced but does not do what one would expect nor does it solve the OP's problem. Nice one. – Marten Oct 05 '12 at 11:50
  • @CQQL Thanks for clarify to me what OP really wanted, I just modified a bit my function to achieve the intended behaviour. You might want to check it out as it uses a more simple approach than yours. – Nelson Oct 05 '12 at 12:04
  • @myfashionhub It's a shortcut for `if (nth - 1 == 0)` You can learn about pre-increment and post-increment operators here as well http://stackoverflow.com/a/4706225/352672 – Nelson Dec 22 '15 at 01:48
9

indexOf takes a second argument, the character index in the string to begin the search.

function nthChar(string, character, n){
    var count= 0, i=0;
    while(count<n && (i=string.indexOf(character,i)+1)){
        count++;
    }
    if(count== n) return i-1;
    return NaN;
}

var s= 'abcbbasdbgasdnnaabaasdert';

nthChar(s,'a',7);
kennebec
  • 102,654
  • 32
  • 106
  • 127
3

A maybe clearer function. Recursive and copy the mechanism of indexOf:

  • Doesn't cause an error if an incorrect number for nth (ie <= 0). It will return -1 like you can give a negative number (or greater than the length of the string) asfromIndex to indexOf.
  • Can take a fromIndex argument (the same than for indexOf: An integer representing the index at which to start the search; the default value is 0.)

function indexOfNth (string, char, nth, fromIndex=0) {
  let indexChar = string.indexOf(char, fromIndex);
  if (indexChar === -1){
    return -1;
  } else if (nth === 1) {
    return indexChar;
  } else {
    return indexOfNth(string, char, nth-1, indexChar+1);
  }
}


let test = 'string for research purpose';
console.log('first s:', indexOfNth(test, 's', 1));
console.log('second s:', indexOfNth(test, 's', 2));
console.log('15th s:', indexOfNth(test, 's', 15));
console.log('first z:', indexOfNth(test, 'z', 1));
console.log('-1th s:', indexOfNth(test, 's', -1));
console.log('first s starting from index=1:', indexOfNth(test, 's', 1, 1));
JackRed
  • 1,188
  • 2
  • 12
  • 28
2

So a nice way to do this is to extend the string class like so:

(function() {
  String.prototype.nthOccurrenceIndex = function(charToMatch, occurrenceIndex) {
    var char, index, matches, _i, _len;
    matches = 0;
    index = 0;
    for (_i = 0, _len = this.length; _i < _len; _i++) {
      char = this[_i];
      if (char === charToMatch) {
        matches += 1;
        if (matches === occurrenceIndex) {
          return index;
        }
      }
      index += 1;
    }
    return -1;
  };

}).call(this);

The much more concise CoffeeScript version:

String.prototype.nthOccurrenceIndex = (charToMatch, occurrenceIndex)->
  matches = 0
  index = 0

  for char in @
    if char is charToMatch
      matches += 1

      return index if matches is occurrenceIndex

    index += 1

  -1

So now you can do stuff like:

"abcabc".nthOccurrenceIndex('a', 1)
# -> 0

"abcabc".nthOccurrenceIndex('a', 2)
# -> 3

"abcabc".nthOccurrenceIndex('a', 3)
# -> -1

foomip
  • 574
  • 7
  • 7
0
function nthIndexOf(search, n) {
    var myArray = []; 
    for(var i = 0; i < myStr.length; i++) {
        if(myStr.slice(i, i + search.length) === search) {
            myArray.push(i);            
        }
    }   
    return myArray[n - 1];
}
Sharon Choe
  • 409
  • 3
  • 8