I would like to refactor my method. I also need to get which value was first? So which anyOf? Is it possible to get it from here?
Example:
List<string> anyOf = new List<string>(){"at", "near", "by", "above"};
string source = "South Branch Raritan River near High Bridge at NJ"
public static int IndexOfAny(this string source, IEnumerable<string> anyOf, StringComparison stringComparisonType = StringComparison.CurrentCultureIgnoreCase)
{
var founds = anyOf
.Select(sub => source.IndexOf(sub, stringComparisonType))
.Where(i => i >= 0);
return founds.Any() ? founds.Min() : -1;
}
I would like to get back what is first in string. "near" or "at".
CodePudding user response:
You could use:
public static (int index, string? firstMatch) IndexOfAny(this string source, IEnumerable<string> anyOf, StringComparison stringComparisonType = StringComparison.CurrentCultureIgnoreCase)
{
return anyOf
.Select(s => (Index: source.IndexOf(s, stringComparisonType), String: s))
.Where(x => x.Index >= 0)
.DefaultIfEmpty((-1, null))
.First();
}
CodePudding user response:
I couldn't resist creating a more efficient implementation.
Whilst this looks more complicated, its better because,
It allocates only,
- an array for the valid search terms,
- a array of indices for each search term and,
- an array of lengths for each search term.
The source text is enumerated only once and, if a match is found, that loop will exit early.
Additionally, the code incorporates parameter checking which you'll want as extension methods should be resusable.
public static class Extensions
{
public static int IndexOfAny<T>(
this IEnumerable<T> source,
IEnumerable<IEnumerable<T>> targets,
IEqualityComparer<T> comparer = null)
{
// Parameter Handling
comparer = comparer ?? EqualityComparer<T>.Default;
ArgumentNullException.ThrowIfNull(targets);
var clean = targets
.Where(t => t != null)
.Select(t => t.ToArray())
.Where(t => t.Length > 0)
.ToArray();
if (clean.Length == 0)
{
throw new ArgumentException(
$"'{nameof(targets)}' does not contain a valid search sequence");
}
// Prep
var lengths = clean.Select(t => t.Length).ToArray();
var indices = clean.Select(_ => 0).ToArray();
int i = 0;
// Process
foreach(var t in source)
{
i ;
for(var j = 0; j < clean.Length; j )
{
var index = indices[j];
if (comparer.Equals(clean[j][index], t))
{
index = 1;
if (index == lengths[j])
{
return i - lengths[j];
}
indices[j] = index;
}
else
{
if (index != 0)
{
indices[j] = 0;
}
}
}
}
return -1;
}
public static int IndexOfAny(
this string source,
IEnumerable<string> targets,
StringComparer comparer = null)
{
comparer = comparer ?? StringComparer.Ordinal;
ArgumentNullException.ThrowIfNull(targets);
return source.ToCharArray().IndexOfAny(
targets.Select(t => t.ToCharArray()),
new CharComparerAdapter(comparer));
}
}
public class CharComparerAdapter : IEqualityComparer<char>
{
private StringComparer Comparer { get; }
public CharComparerAdapter(StringComparer comparer)
{
ArgumentNullException.ThrowIfNull(comparer);
Comparer = comparer;
}
public bool Equals(char left, char right)
{
return Comparer.Equals(left.ToString(), right.ToString());
}
public int GetHashCode(char v)
{
return v;
}
}