Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# equivalent of C++ std::string find_first_not_of and find_last_not_of

Tags:

string

c#

IndexOf, IndexOfAny and LastIndexOf, LastIndexOfAny dont seem to do these (or maybe they do). I'm looking for the equialent of std::string's find_first_not_of and find_last_not_of. I'm thinking about creating an extension class but I'm not sure if C# already provides this functionality.

like image 218
Marlon Avatar asked Dec 21 '10 10:12

Marlon


2 Answers

string source = "the quick brown fox jumps over the lazy dog";
string chars = "ogd hte";

int? firstNotOf = source.Select((x, i) => new { Val = x, Idx = (int?)i })
                        .Where(x => chars.IndexOf(x.Val) == -1)
                        .Select(x => x.Idx)
                        .FirstOrDefault();

int? lastNotOf = source.Select((x, i) => new { Val = x, Idx = (int?)i })
                       .Where(x => chars.IndexOf(x.Val) == -1)
                       .Select(x => x.Idx)
                       .LastOrDefault();

Or, if you prefer some non-LINQ extension methods. These should have slightly better performance, especially for FindLastNotOf:

int? firstNotOf = source.FindFirstNotOf(chars);
int? lastNotof = source.FindLastNotOf(chars);

// ...

public static int? FindFirstNotOf(this string source, string chars)
{
    if (source == null) throw new ArgumentNullException("source");
    if (chars == null) throw new ArgumentNullException("chars");
    if (source.Length == 0) return null;
    if (chars.Length == 0) return 0;

    for (int i = 0; i < source.Length; i++)
    {
        if (chars.IndexOf(source[i]) == -1) return i;
    }
    return null;
}

public static int? FindLastNotOf(this string source, string chars)
{
    if (source == null) throw new ArgumentNullException("source");
    if (chars == null) throw new ArgumentNullException("chars");
    if (source.Length == 0) return null;
    if (chars.Length == 0) return source.Length - 1;

    for (int i = source.Length - 1; i >= 0; i--)
    {
        if (chars.IndexOf(source[i]) == -1) return i;
    }
    return null;
}

(It's possible that you might get better performance -- in both the LINQ and non-LINQ versions -- if you convert chars to a HashSet<char>, or maybe even a plain char[] array. You'd need to benchmark to find out, though any difference is likely to be negligible unless chars gets pretty big.)

like image 168
LukeH Avatar answered Oct 19 '22 18:10

LukeH


If the use of LINQ is acceptable, you can call the First() and Last() methods with the appropriate predicate.

For instance, if you want the first and last characters that aren't vowels:

string vowels = "aeiouy";
char first = yourString.First(ch => vowels.IndexOf(ch) < 0);
char last = yourString.Last(ch => vowels.IndexOf(ch) < 0);

EDIT: The above will return the characters, not their indexes. In order to do that, you can project the indexes using the Select() method, but things will get hairy since we need to return -1 if no character matches:

int firstIndex = (yourString.Select(
        (ch, i) => new { Character = ch, Index = i }
    ).First(obj => vowels.IndexOf(obj.Character) < 0)
    ?? new { Character = '\0', Index = -1 }).Index;

int lastIndex = (yourString.Select(
        (ch, i) => new { Character = ch, Index = i }
    ).Last(obj => vowels.IndexOf(obj.Character) < 0)
    ?? new { Character = '\0', Index = -1 }).Index;

Alternatively, here's a less complicated solution based on @abatishchev's answer:

string vowels = "aeiouy";
int firstIndex = yourString.IndexOf(yourString.First(
    ch => vowels.IndexOf(ch) < 0));
int lastIndex = yourString.LastIndexOf(yourString.Last(
    ch => vowels.IndexOf(ch) < 0));
like image 24
Frédéric Hamidi Avatar answered Oct 19 '22 19:10

Frédéric Hamidi