我们来看一个叫做Cls的课:
公共类Cls{ public int SequenceNumber {get;组; } public int Value {get;组; }}
现在,让我们使用以下元素填充一些集合:
Seq
你可以这样做:
var all = new [] { new Cls(1, 9) , new Cls(2, 9) , new Cls(3, 15) , new Cls(4, 15) , new Cls(5, 15) , new Cls(6, 30) , new Cls(7, 9) }; var f = all.First(); var res = all.Skip(1).Aggregate( new List<Run> {new Run {From = f.SequenceNumber, To = f.SequenceNumber, Value = f.Value} } , (p, v) => { if (v.Value == p.Last().Value) { p.Last().To = v.SequenceNumber; } else { p.Add(new Run {From = v.SequenceNumber, To = v.SequenceNumber, Value = v.Value}); } return p; }); foreach (var r in res) { Console.WriteLine("{0} - {1} : {2}", r.From, r.To, r.Value); }
这个想法是用的 Aggregate 创造性地:从包含单个列表的列表开始 Run ,检查我们到目前为止在每个聚合阶段所获得的列表的内容( if lambda中的陈述)。根据最后一个值,继续旧运行或启动新运行。
Aggregate
Run
if
这里有一个 关于ideone的演示 。
我能够通过创建自定义扩展方法来实现它。
static class Extensions { internal static IEnumerable<Tuple<int, int, int>> GroupAdj(this IEnumerable<Cls> enumerable) { Cls start = null; Cls end = null; int value = Int32.MinValue; foreach (Cls cls in enumerable) { if (start == null) { start = cls; end = cls; continue; } if (start.Value == cls.Value) { end = cls; continue; } yield return Tuple.Create(start.SequenceNumber, end.SequenceNumber, start.Value); start = cls; end = cls; } yield return Tuple.Create(start.SequenceNumber, end.SequenceNumber, start.Value); } }
这是实施:
static void Main() { List<Cls> items = new List<Cls> { new Cls { SequenceNumber = 1, Value = 9 }, new Cls { SequenceNumber = 2, Value = 9 }, new Cls { SequenceNumber = 3, Value = 15 }, new Cls { SequenceNumber = 4, Value = 15 }, new Cls { SequenceNumber = 5, Value = 15 }, new Cls { SequenceNumber = 6, Value = 30 }, new Cls { SequenceNumber = 7, Value = 9 } }; Console.WriteLine("From To Value"); Console.WriteLine("===== ===== ====="); foreach (var item in items.OrderBy(i => i.SequenceNumber).GroupAdj()) { Console.WriteLine("{0,-5} {1,-5} {2,-5}", item.Item1, item.Item2, item.Item3); } }
和预期的产量:
From To Value ===== ===== ===== 1 2 9 3 5 15 6 6 30 7 7 9
未经考验的黑魔法随之而来。在这种情况下,命令式版本似乎更容易。
IEnumerable<Cls> data = ...; var query = data .GroupBy(x => x.Value) .Select(g => new { Value = g.Key, Sequences = g .OrderBy(x => x.SequenceNumber) .Select((x,i) => new { x.SequenceNumber, OffsetSequenceNumber = x.SequenceNumber - i }) .GroupBy(x => x.OffsetSequenceNumber) .Select(g => g .Select(x => x.SequenceNumber) .OrderBy(x => x) .ToList()) .ToList() }) .SelectMany(x => x.Sequences .Select(s => new { First = s.First(), Last = s.Last(), x.Value })) .OrderBy(x => x.First) .ToList();
这是一个没有任何辅助方法的实现:
var grp = 0; var results = from i in input.Zip( input.Skip(1).Concat(new [] {input.Last ()}), (n1, n2) => Tuple.Create( n1, (n2.Value == n1.Value) ? grp : grp++ ) ) group i by i.Item2 into gp select new {SequenceNumFrom = gp.Min(x => x.Item1.SequenceNumber),SequenceNumTo = gp.Max(x => x.Item1.SequenceNumber), Value = gp.Min(x => x.Item1.Value)};
这个想法是: