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.
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.)
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));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With