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?
1
  • 2
    Simply convert your code to a loop. Commented Oct 5, 2012 at 11:14

7 Answers 7

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);
Sign up to request clarification or add additional context in comments.

4 Comments

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).
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.
the return string was not in a single line so it was showing the undefined output.was my mistake.It worked thank you.
@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
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.

5 Comments

str.charAt(i) is the same as str[i] - but your code counts occurrences, it doesn't find the nth occurrence.
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.
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.
@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.
@myfashionhub It's a shortcut for if (nth - 1 == 0) You can learn about pre-increment and post-increment operators here as well stackoverflow.com/a/4706225/352672
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);

Comments

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

1 Comment

that's very smart and cool solution, should be accepted one IMHO.
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

Comments

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];
}

Comments

0

May I humbly suggest:

     const nthChar = (str, ch, nth=1, pos=0) => {
        --pos;
        while ((nth-- > 0) && ((pos=str.indexOf(ch, pos+1)) >= pos));
        return (nth > 0)? -1 : pos;
     }

str: the string you are inquiring about;

ch: the character to find;

nth: the nth-occurence of it (default to 1);

pos: the position in str where to start from (default to 0);

A few examples, and tests

  Welcome to Node.js v18.17.1.
  Type ".help" for more information.
  >          const nthChar = (s, ch, nth=1, pos=0) => {
  ...             --pos;
  ...             while ((nth-- > 0) && ((pos=s.indexOf(ch, pos+1)) >= pos));
  ...             return (nth > 0)? -1 : pos;
  ...          }
  undefined
  > nthChar("Hello World", "l", 3);
  9
  > nthChar("Hello World", "l", 2);
  3
  > nthChar("Hello World", "W", 1);
  6
  > nthChar("Hello World", "W", 1, 4);
  6
  > nthChar("Hello World", "l");
  2
  > nthChar("Hello World", "l", 1);
  2
  > nthChar("Hello World", "l");
  2
  > nthChar("Hello World", "X");
  -1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.