给定序列:
["1","A","B","C","2","F","K","L","5","6","P","I","E"]
数字表示我标识为标题的项目,而字母表示我标识为数据的项目。我想把它们分成这样的组。
1:A,B,C
2:F,K,L
5:
6:P,I,E
我可以使用枚举器上的foreach或while循环轻松实现此目的,但有没有LINQ'ish方法来实现这一点?这是我网域中反复出现的模式。
答案 0 :(得分:3)
这是LINQ的解决方案。虽然这有点复杂。一些技巧可能存在空间。它看起来并不那么糟糕,但它可以通过foreach循环更具可读性。
int lastHeaderIndex = default(int);
Dictionary<string, IEnumerable<string>> groupedItems =
items.Select((text, index) =>
{
int number;
if (int.TryParse(text, out number))
{
lastHeaderIndex = index;
}
return new { HeaderIndex = lastHeaderIndex, Value = text };
})
.GroupBy(item => item.HeaderIndex)
.ToDictionary(item => item.FirstOrDefault().Value,
item => item.Skip(1).Select(arg => arg.Value));
答案 1 :(得分:2)
foreach
的 int.TryParse
循环应该会有所帮助。来自LINQ的'GroupBy'在这方面没有多大帮助。
答案 2 :(得分:2)
你可以使用折叠:
var aggr = new List<Tuple<Int,List<String>>>();
var res = sequence.Aggregate(aggr, (d, x) => {
int i;
if (Int32.TryParse(x, out i)) {
var newDict = d.Add(new Tuple(i, new List<string>()));
return newDict;
}
else {
var newDict = d[d.Count - 1].Item2.Add(x);
return newDict;
}
}).ToDictionary(x => x.Item1, x => x.Item2);
然而,这看起来并不那么好,因为缺乏对不可变值的支持。另外,我现在无法测试。
答案 3 :(得分:2)
由于这是您域中的常见模式,因此请考虑对结果进行流式处理,而不是将它们全部收集到大型内存中对象中。
public static IEnumerable<IList<string>> SplitOnToken(IEnumerable<string> input, Func<string,bool> isSplitToken)
{
var set = new List<string>();
foreach(var item in input)
{
if (isSplitToken(item) && set.Any())
{
yield return set;
set = new List<string>();
}
set.Add(item);
}
if (set.Any())
{
yield return set;
}
}
样本用法:
var sequence = new[] { "1", "A", "B", "C", "2", "F", "K", "L", "5", "6", "P", "I", "E" };
var groups = SplitOnToken(sequence, x => Char.IsDigit(x[0]));
foreach (var @group in groups)
{
Console.WriteLine("{0}: {1}", @group[0], String.Join(" ", @group.Skip(1).ToArray()));
}
输出:
1: A B C
2: F K L
5:
6: P I E
答案 4 :(得分:1)
这是我最终使用的内容。与phg的答案几乎相同的结构。
基本上,它是一个聚合函数,它维护一个包含以下内容的元组: 1:累积的数据。 2:解析器的状态。
聚合函数执行if-else检查当前检查的项目是组头还是常规项。基于此,它更新数据存储区(元组的最后一部分)和/或更改解析器状态(元组的第一部分)。
就我而言,解析器状态是当前活动的列表(即将插入的项目)。
var sequence = new[]{ "1","A","B","C","2","F","K","L","5","6","P","I","E"};
var aggr = Tuple.Create(new List<string>(), new Dictionary<int,List<string>>());
var res = sequence.Aggregate(aggr, (d, x) => {
int i;
if (Int32.TryParse(x, out i))
{
var newList = new List<string>();
d.Item2.Add(i,newList);
return Tuple.Create(newList,d.Item2);
} else
{
d.Item1.Add(x);
return d;
}
},d=>d.Item2);