LINQ查找第一个可用的带数字后缀的字符串

时间:2018-07-22 07:08:33

标签: c# list linq

给出一个名称列表,例如{Name1Name2Name3Name10},我想找到第一个可用的名称,在这种情况下,是Name4,但是如果名称是连续的,则下一个可用的应该是Name5

列表未排序。

我发现Efficient algorithm to find first available name似乎有一些不错的算法,但是我不确定是否可以在LINQ中实现。

有可能吗?哪种方法最有效?

5 个答案:

答案 0 :(得分:3)

MichałTurczyn代码的变体,处理空集和无序集。前缀可以在变量中轻松更改。

string prefix = "Name";

int i = 0;
var available = names
    .Select(x => int.Parse(x.Substring(prefix.Length)))
    .OrderBy(x => x)
    .TakeWhile(x => x == ++i)
    .DefaultIfEmpty(0)
    .Select(x => prefix + (x + 1))
    .Last();

请注意,较不纯正的LINQ版本(但更快,因为它不必生成所有各种Select,它是:

int available = names.Select(x => int.Parse(x.Substring(prefix.Length)))
                     .OrderBy(x => x)
                     .TakeWhile(x => x == ++i)
                     .LastOrDefault() + 1;

string available2 = prefix + available;

没有外部变量i(当linq具有外部状态时,这总是很丑):

int available = (Enumerable.Range(1, int.MaxValue - 1)
    .Zip(names.Select(x => int.Parse(x.Substring(prefix.Length)))
        .OrderBy(x => x), (x, y) => new { x, y })
    .TakeWhile(x => x.x == x.y).LastOrDefault() ?? new { x = 0, y = 0 }).x + 1;

请注意,使用Enumerable.RangeZip创建了要比较的次级序列1 ... n。

答案 1 :(得分:1)

尝试一下:

var names = new List<string>{ "Name1", "Name10", "Name3", "Name2" };
var hs = new HashSet<int>(names.Select(n => int.Parse(n.Substring("Name".Length))));
var available = $"Name{Enumerable.Range(1, 255).Where(n => !hs.Contains(n)).First()}";

这将为Name4提供正确的结果。如果未对原始列表进行排序,则速度要尽可能快。

答案 2 :(得分:1)

private static readonly HashSet<string> Names = new HashSet<string> { "Name1", "Name10", "Name3", "Name2" };

static void Main(string[] args)
{
    int index = Enumerable.Range(1, 255).FirstOrDefault(i => !IsNameAvailable(i));

    if (index == default(int))
    {
        Console.WriteLine("All names are available!!!");
    }
    else
    {
        Console.WriteLine($"First non available name: Name{index}");
    }

    Console.Read();
}

private static bool IsNameAvailable(int index)
{
    return Names.Contains($"Name{index}");
}

输出:

  

第一个无效名称:Name4

答案 3 :(得分:0)

好吧,这可以使用LINQ来实现,但是它有一些条件(至少是我的解决方案):

它必须具有辅助整数,以检查索引(附加在字符串上的连续数字)。

int i = 0;
string prefix = "Name";
List<string> names = new List<string>{ "Name1", "Name2", "Name3", "Name10" };
//names.Sort();
string available = names
    .Select(n => new { name = n, index = int.Parse(n.Substring(prefix.Length)) })
    .OrderBy(a => a.index)
    .TakeWhile(a => { i++; return a.index == i; })
    .Select(a => prefix + (a.index + 1))
    .Last();

答案 4 :(得分:0)

我不知道我是否正确理解了问题,我怀疑的原因是因为除了我之外,所有答题者似乎都太合格了,他们的观点吓到了我,但这就是我想出的< / p>

List<string> existingNames = new List<string>() { "Name1", "Name2", "Name3", "Name10" };
var availableNameList = Enumerable.Range(1, 255)
                       .Except
                        ( 
                         existingNames
                         .Select(x => Int32.Parse(Regex.Match(x, @"\d+").Value))
                        )
                        .OrderBy(x=>x)
                        .Select(x=>"Name"+x)
                        .ToList();