Search code examples
c#linqunique

Unique Name given list and seed


My problem is to generate a new unique Name in a list composed by 'seed' a sequential integer number, reusing missing indexes with same seed.

I wrote down following extension

    /// <summary>
    /// Gets the Next Valid Index given a list of names and a root string.
    /// </summary>
    /// <param name="names">The list of just used names.</param>
    /// <param name="seedName">The string to be used as seed.</param>
    /// <returns>System.Int32 (One Based) with next valid Index.</returns>
    /// <Examble>
    /// Present Names:
    /// AA, AA1, AA2, BB, BB3
    /// With Seed AA, result is 3
    /// With Seed BB, result is 1
    /// With Seed CC, result is 1 (missing index is used)
    /// </Examble>
    public static int GetNextIndex(this List<string> names, string seedName)
    {
        var max = 0;
        var namesIndexes = new List<NameIndex>();

        names.ForEach(name =>
        {
            var m = Regex.Match(name, @"\d+");
            if (m.Success)
            {
                if (string.IsNullOrWhiteSpace(m.Value))
                {
                    namesIndexes.Add(new NameIndex(name, 0));
                }
                else
                {
                    var v = 0;
                    if (int.TryParse(m.Value, out v))
                        namesIndexes.Add(new NameIndex(name.Replace(m.Value, string.Empty), v));
                }
            }
            else
            {
                namesIndexes.Add(new NameIndex(name, 0));
            }
        });

        var grouped = namesIndexes.GroupBy(n => n.Name).ToDictionary(g => g.Key, g => g);
        if (grouped.ContainsKey(seedName))
        {
            max = grouped[seedName].Max(gn => gn.Index);
            var all = Enumerable.Range(1, max).ToList();
            var available = all.Except(grouped[seedName].Select(gn => gn.Index)).ToList();
            if (available.Any())
                return available.Min();
        }

        return max + 1;
    }

Where NameIndex is an Helper Class:

/// <summary>
/// Class NameIndex.
/// </summary>
public class NameIndex
{
    public string Name { get; set; }
    public int Index { get; set; }

    public NameIndex(string name, int index)
    {
        Name = name;
        Index = index;
    }
}

My question is : how you could improve the function?

Thanks for Help!


Solution

  • See if this code works for you

    public static int GetNextIndex(List<string> names, string seedName)
    {
        var namesWithSeed = names.Where(n => !string.IsNullOrEmpty(n) && n.StartsWith(seedName));
        if (names.Count == 0)
            return 1;
        int temp = 0;
        var allIntsStringsForSeed = namesWithSeed
                                    .Select(n => n.Replace(seedName, string.Empty))
                                    .Select(s => int.TryParse(s, out temp) ? temp : 0)
                                    .OrderBy(i => i)
                                    .ToList();
        var idx = 0;
        for (int i = 0; i < allIntsStringsForSeed.Count; i++)
        {
            if (i == allIntsStringsForSeed.ElementAt(i))
                idx = i;
            else
                break;
        }
        return idx == 0 ? 1 : idx + 1;
    }
    

    Test

    var strings = new List<string> { "AA", "AA1", "AA2", "BB", "BB3" };
    var indexAA = GetNextIndex(strings, "AA"); //3
    var indexBB = GetNextIndex(strings, "BB"); //1
    var indexCC = GetNextIndex(strings, "CC"); //1