给出一个名称列表,例如{Name1
,Name2
,Name3
,Name10
},我想找到第一个可用的名称,在这种情况下,是Name4
,但是如果名称是连续的,则下一个可用的应该是Name5
。
列表未未排序。
我发现Efficient algorithm to find first available name似乎有一些不错的算法,但是我不确定是否可以在LINQ中实现。
有可能吗?哪种方法最有效?
答案 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.Range
和Zip
创建了要比较的次级序列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();