public int GetNthIndex(string s, char t, int n)
{
int count = 0;
for (int i = 0; i < s.Length; i++)
{
if (s[i] == t)
{
count++;
if (count == n)
{
return i;
}
}
}
return -1;
}
That could be made a lot cleaner, and there are no checks on the input.
string input = "dtststx";
char searchChar = 't';
int occurrencePosition = 3; // third occurrence of the char
var result = input.Select((c, i) => new { Char = c, Index = i })
.Where(item => item.Char == searchChar)
.Skip(occurrencePosition - 1)
.FirstOrDefault();
if (result != null)
{
Console.WriteLine("Position {0} of '{1}' occurs at index: {2}",
occurrencePosition, searchChar, result.Index);
}
else
{
Console.WriteLine("Position {0} of '{1}' not found!",
occurrencePosition, searchChar);
}
Just for fun, here's a Regex solution. I saw some people initially used Regex to count, but when the question changed no updates were made. Here is how it can be done with Regex - again, just for fun. The traditional approach is best for simplicity.
string input = "dtststx";
char searchChar = 't';
int occurrencePosition = 3; // third occurrence of the char
Match match = Regex.Matches(input, Regex.Escape(searchChar.ToString()))
.Cast<Match>()
.Skip(occurrencePosition - 1)
.FirstOrDefault();
if (match != null)
Console.WriteLine("Index: " + match.Index);
else
Console.WriteLine("Match not found!");
public static int FindOccuranceOf(this string str,char @char, int occurance)
{
var result = str.Select((x, y) => new { Letter = x, Index = y })
.Where(letter => letter.Letter == @char).ToList();
if (occurence > result.Count || occurance <= 0)
{
throw new IndexOutOfRangeException("occurance");
}
return result[occurance-1].Index ;
}
Here is a recursive implementation - as an extension method, mimicing the format of the framework method(s):
public static int IndexOfNth(
this string input, string value, int startIndex, int nth)
{
if (nth < 1)
throw new NotSupportedException("Param 'nth' must be greater than 0!");
if (nth == 1)
return input.IndexOf(value, startIndex);
return input.IndexOfNth(value, input.IndexOf(value, startIndex) + 1, --nth);
}
Also, here are some (MBUnit) unit tests that might help you (to prove it is correct):
ranomore correctly commented that Joel Coehoorn's one-liner doesn't work.
Here is a two-liner that does work, a string extension method that returns the 0-based index of the nth occurrence of a character, or -1 if no nth occurrence exists:
public static class StringExtensions
{
public static int NthIndexOf(this string s, char c, int n)
{
var takeCount = s.TakeWhile(x => (n -= (x == c ? 1 : 0)) > 0).Count();
return takeCount == s.Length ? -1 : takeCount;
}
}
Hi all i have created two overload methods for finding nth occurrence of char and for text with less complexity without navigating through loop ,which increase performance of your application.
public static int NthIndexOf(string text, char searchChar, int nthindex)
{
int index = -1;
try
{
var takeCount = text.TakeWhile(x => (nthindex -= (x == searchChar ? 1 : 0)) > 0).Count();
if (takeCount < text.Length) index = takeCount;
}
catch { }
return index;
}
public static int NthIndexOf(string text, string searchText, int nthindex)
{
int index = -1;
try
{
Match m = Regex.Match(text, "((" + searchText + ").*?){" + nthindex + "}");
if (m.Success) index = m.Groups[2].Captures[nthindex - 1].Index;
}
catch { }
return index;
}
Since the built-in IndexOf function is already optimized for searching a character within a string, an even faster version would be (as extension method):
public static int NthIndexOf(this string input, char value, int n)
{
if (n <= 0) throw new ArgumentOutOfRangeException("n", n, "n is less than zero.");
int i = -1;
do
{
i = input.IndexOf(value, i + 1);
n--;
}
while (i != -1 && n > 0);
return i;
}
Or to search from the end of the string using LastIndexOf:
public static int NthLastIndexOf(this string input, char value, int n)
{
if (n <= 0) throw new ArgumentOutOfRangeException("n", n, "n is less than zero.");
int i = input.Length;
do
{
i = input.LastIndexOf(value, i - 1);
n--;
}
while (i != -1 && n > 0);
return i;
}
Searching for a string instead of a character is as simple as changing the parameter type from char to string and optionally add an overload to specify the StringComparison.
public int GetNthOccurrenceOfChar(string s, char c, int occ)
{
return String.Join(c.ToString(), s.Split(new char[] { c }, StringSplitOptions.None).Take(occ)).Length;
}
string result = "i am 'bansal.vks@gmail.com'"; // string
int in1 = result.IndexOf('\''); // get the index of first quote
int in2 = result.IndexOf('\'', in1 + 1); // get the index of second
string quoted_text = result.Substring(in1 + 1, in2 - in1); // get the string between quotes
using System;
using System.Collections.Generic;
using System.Linq;
namespace fNns
{
public class indexer<T> where T : IEquatable<T>
{
public T t { get; set; }
public int index { get; set; }
}
public static class fN
{
public static indexer<T> findNth<T>(IEnumerable<T> tc, T t,
int occurrencePosition) where T : IEquatable<T>
{
var result = tc.Select((ti, i) => new indexer<T> { t = ti, index = i })
.Where(item => item.t.Equals(t))
.Skip(occurrencePosition - 1)
.FirstOrDefault();
return result;
}
public static indexer<T> findNthReverse<T>(IEnumerable<T> tc, T t,
int occurrencePosition) where T : IEquatable<T>
{
var result = tc.Reverse<T>().Select((ti, i) => new indexer<T> {t = ti, index = i })
.Where(item => item.t.Equals(t))
.Skip(occurrencePosition - 1)
.FirstOrDefault();
return result;
}
}
}
Some tests.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Newtonsoft.Json;
namespace FindNthNamespace.Tests
{
public class fNTests
{
[TestCase("pass", "dtststx", 't', 3, Result = "{\"t\":\"t\",\"index\":5}")]
[TestCase("pass", new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
0, 2, Result="{\"t\":0,\"index\":10}")]
public string fNMethodTest<T>(string scenario, IEnumerable<T> tc, T t, int occurrencePosition) where T : IEquatable<T>
{
Console.WriteLine(scenario);
return JsonConvert.SerializeObject(fNns.fN.findNth<T>(tc, t, occurrencePosition)).ToString();
}
[TestCase("pass", "dtststxx", 't', 3, Result = "{\"t\":\"t\",\"index\":6}")]
[TestCase("pass", new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
0, 2, Result = "{\"t\":0,\"index\":19}")]
public string fNMethodTestReverse<T>(string scenario, IEnumerable<T> tc, T t, int occurrencePosition) where T : IEquatable<T>
{
Console.WriteLine(scenario);
return JsonConvert.SerializeObject(fNns.fN.findNthReverse<T>(tc, t, occurrencePosition)).ToString();
}
}
if your interested you can also create string extension methods like so:
public static int Search(this string yourString, string yourMarker, int yourInst = 1, bool caseSensitive = true)
{
//returns the placement of a string in another string
int num = 0;
int currentInst = 0;
//if optional argument, case sensitive is false convert string and marker to lowercase
if (!caseSensitive) { yourString = yourString.ToLower(); yourMarker = yourMarker.ToLower(); }
int myReturnValue = -1; //if nothing is found the returned integer is negative 1
while ((num + yourMarker.Length) <= yourString.Length)
{
string testString = yourString.Substring(num, yourMarker.Length);
if (testString == yourMarker)
{
currentInst++;
if (currentInst == yourInst)
{
myReturnValue = num;
break;
}
}
num++;
}
return myReturnValue;
}
public static int Search(this string yourString, char yourMarker, int yourInst = 1, bool caseSensitive = true)
{
//returns the placement of a string in another string
int num = 0;
int currentInst = 0;
var charArray = yourString.ToArray<char>();
int myReturnValue = -1;
if (!caseSensitive)
{
yourString = yourString.ToLower();
yourMarker = Char.ToLower(yourMarker);
}
while (num <= charArray.Length)
{
if (charArray[num] == yourMarker)
{
currentInst++;
if (currentInst == yourInst)
{
myReturnValue = num;
break;
}
}
num++;
}
return myReturnValue;
}
Here's another, maybe simpler implementation of string IndexOfNth() with strings implementation.
Here's the string match version:
public static int IndexOfNth(this string source, string matchString,
int charInstance,
StringComparison stringComparison = StringComparison.CurrentCulture)
{
if (string.IsNullOrEmpty(source))
return -1;
int lastPos = 0;
int count = 0;
while (count < charInstance )
{
var len = source.Length - lastPos;
lastPos = source.IndexOf(matchString, lastPos,len,stringComparison);
if (lastPos == -1)
break;
count++;
if (count == charInstance)
return lastPos;
lastPos += matchString.Length;
}
return -1;
}
and the char match version:
public static int IndexOfNth(string source, char matchChar, int charInstance)
{
if (string.IsNullOrEmpty(source))
return -1;
if (charInstance < 1)
return -1;
int count = 0;
for (int i = 0; i < source.Length; i++)
{
if (source[i] == matchChar)
{
count++;
if (count == charInstance)
return i;
}
}
return -1;
}
I think for such a low level implementation you'd want to stay away from using LINQ, RegEx or recursion to reduce overhead.
public static int IndexOfAny(this string str, string[] values, int startIndex, out string selectedItem)
{
int first = -1;
selectedItem = null;
foreach (string item in values)
{
int i = str.IndexOf(item, startIndex, StringComparison.OrdinalIgnoreCase);
if (i >= 0)
{
if (first > 0)
{
if (i < first)
{
first = i;
selectedItem = item;
}
}
else
{
first = i;
selectedItem = item;
}
}
}
return first;
}