假设我有以下数组(我的序列都按升序排序,并包含正整数)
foreach()
我想写一个linq查询来选择被视为一个组的系列中的连续数字。所以,在上面的例子中我会得到{[1,2,3],[7,8,9],[15,16,17]}。
我可以编写一个foreach()
序列,遍历每个元素,看看序列在哪里跳跃并在那里产生一个组。但有没有LINQ唯一的方法呢?我可以将我的Me.Name
代码移动到一个新的扩展方法,所以我的代码仍然看起来像LINQy,但我想知道System.Linq中是否有任何可用的代码。
internal class Sequence
{
public int Start { get; set; }
public int End { get; set; }
}
internal static class EnumerableMixins
{
public static IEnumerable<Sequence> GroupFragments(this IEnumerable<int> sequence)
{
if (sequence.Any())
{
var lastNumber = sequence.First();
var firstNumber = lastNumber;
foreach(var number in sequence.Skip(1))
{
if (Math.Abs(number - lastNumber) > 1)
{
yield return new Sequence() { Start = firstNumber, End = lastNumber };
firstNumber = lastNumber = number;
}
else
{
lastNumber = number;
}
}
yield return new Sequence() { Start = firstNumber, End = lastNumber };
}
}
}
在答案中提出了一些非常聪明的内容。
{{1}}
答案 0 :(得分:10)
找到这些岛屿的一个老技巧是减去索引和数值。结果将代表独特的群体。使用select重载包括索引:
var mySequence = new [] {1, 2, 3, 7, 8, 9, 15, 16, 17};
var groups = mySequence
.Select((val,ind) => new{val, group = val - ind})
.GroupBy(v=>v.group, v=>v.val)
.Select(v=> v.ToList()).ToList();
(在这里使用ToList,但当然如果首选数组则可以使用ToArray)
答案 1 :(得分:1)
我迟到了,但无论如何我想分享这个想法。你也可以创建一个这样的迭代器:
public static class Extensions
{
public static IEnumerable<IEnumerable<int>> ContinuousNumbers(this IEnumerable<int> source)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
var list = new List<int> { e.Current};
int temp = e.Current;
while (e.MoveNext())
{
if (e.Current == temp+1)
{
list.Add(e.Current);
temp++;
}
else
{
yield return list;
list = new List<int> { e.Current};
temp = e.Current;
}
}
if (list.Count > 0)
{
yield return list;
}
}
}
}
}
另一种变体可能是使用Aggregate扩展方法:
var result = mySequence.Aggregate(new List<List<int>>(),
(r, current) =>
{
if ( r.Count==0 || (r.Last().Count>0 && r.Last().Last() != current-1))
r.Add(new List<int> { current});
else
r.Last().Add(current);
return r;
});