Take a desired string, iterate through objects to see if it exists in a given field and append a number until a unique string is found I wanted to in the most generic way possible write a method in C# to achieve the following.


Take in a string, a set of objects, and a function to access the field of a given object
Look at all of the strings from the fields of these objects to match against provided string
If provided string is not unique in a case insensitive manner, append _x to the end, where x is an incrementing integer until a unique string is found


With that in mind, I created this.

private string GetUniqueStringForModel&lt;T&gt;(string originalString, IEnumerable&lt;T&gt; enumerableObjects, Func&lt;T, string&gt; stringFieldFunction) where T : class
{
    var uniqueString = originalString;
    var duplicateCount = 1;

    while (enumerableObjects.Select(stringFieldFunction).ToList().Any(currentString =&gt; string.Equals(currentString, uniqueString, StringComparison.InvariantCultureIgnoreCase)))
    {
        uniqueString = originalString + "_" + duplicateCount++;
    }

    return uniqueString;
}


I would rather have not put the ToList() in but when working with Entity Framework it was complaining about doing the string comparison in LINQ (presumably because it couldn't compile it SQL).

Any thoughts or ideas for improvement?
There's certainly room for improvement.


Move that that Select(stringFieldFunction).ToList() out of the while loop. Iterating an IEnumerable might be quite expensive, and there's no need to repeat that work when you find a duplicate.
Select only those strings that have originalString as a prefix. That will reduce the amount of items you need to check against when adjusting the suffix.
Instead of making a list, turn those results into a (hash)set, for faster lookup.


Regarding EF complaining: apparently it doesn't support those StringComparsion overloads, so it'll have to fetch all rows from the database and run your Any predicate in memory, which may be slower as expected. Maybe an alternative approach like using ToUpperInvariant can be translated to SQL?



Applying the above changes will give you the following:

private string GetUniqueValue&lt;T&gt;(string value, IEnumerable&lt;T&gt; items, Func&lt;T, string&gt; getValue)
{
    var possibleDuplicates = items
        .Select(getValue)
        .Where(val =&gt; val.StartsWith(value, StringComparison.InvariantCultureIgnoreCase))
        .ToHashSet();

    var result = value;
    var suffix = 1;
    while (possibleDuplicates.Contains(result))
    {
        result = value + "_" + suffix;
        suffix += 1;
    }
    return result;
}




Addendum

As t3chb0t and ErikE already pointed out, if you want the benefits of Linq-to-SQL, then you need to use IQueryable&lt;T&gt;. Not only that, you also need to make sure that you're using the Queryable Linq extension methods, not the Enumerable variants.

The difference is that Enumerable methods have Func&lt;&gt; parameters, while the Queryable variants have Expression&lt;Func&lt;&gt;&gt; parameters. An Expression is a data structure that represents a piece of code, which makes translation to SQL possible. Because stringFieldFunction is a Func, not an Expression&lt;Func&gt;, the Enumerable.Select variant is used. That cannot be translated to SQL, so all data has to be loaded from the database before the Select (and any subsequent operation) can be performed on it.

To recap: use IQuerable&lt;T&gt;, make sure you're using Queryable Linq methods, and only use supported methods within your expressions.