Home > other >  C# get also the key from parsing substring from string
C# get also the key from parsing substring from string

Time:11-17

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.

Working here.

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;
    }
}
  •  Tags:  
  • c#
  • Related