我为此寻找过,我确信我只是错过了它,因为我对Linq并不那么好。
我有一个类似的列表:
type=a, value=aaaa
type=a, value=bbbb
type=b, value=cccc
type=d, value=dddd
type=d, value=eeee
type=d, value=ffff
type=a, value=gggg
type=b, value=hhhh
type=b, value=iiii
type=b, value=jjjj
我想将其分解为子列表,而不进行排序(我需要维护原始订单)。我希望以列表或类似内容的顺序取回这些列表:
List 1
type=a, value=aaaa
type=a, value=bbbb
List2
type=b, value=cccc
List 3
type=d, value=dddd
type=d, value=eeee
type=d, value=ffff
List 4
type=a, value=gggg
List 5
type=b, value=hhhh
type=b, value=iiii
type=b, value=jjjj
我认为循环不是最好的答案。
任何想法都非常感激。
万岁stackoverflow.com!
克里斯
四个答案后编辑:
我检查了答案: *电子化 *伯特埃文斯 *风险马丁 * david.s
他们都很好地工作。 Bert Evans带来了性能,在这种情况下对我来说并不是一个大问题,但为了帖子我做了一些快速检查。
我没有修改任何人的代码,只是计划在相当短的列表上执行4,000个这样的操作。
风险的回答是最快的。 伯特的答案只是慢一点。
david's和Enigmativity几乎没有任何慢,真的。
我将Risky的答案标记为因为表现而被接受,并在早期指向相关帖子,然后回来提供答案。
我同意伯特是最可读的。
老实说,我不知道我将使用哪一个......实际上,我将使用Enigmativity的解决方案,因为它已经考虑到我只需要每个子组的值和一个键。
答案 0 :(得分:4)
public static IEnumerable<IGrouping<int, T>> GroupConsecutive<T>(this IEnumerable<T> set, Func<T, T, bool> predicate)
{
var i = 0;
var k = 0;
var ranges = from e in set
let idx = ++i
let next = set.ElementAtOrDefault(idx)
let key = next == null ? k : predicate(e, next) ? k : k++
group e by key into g
select g;
return ranges;
}
并给出了一个课程:
public class Foo
{
public string Type { get; set; }
public string Value { get; set; }
}
你可以这样做:
List<Foo> list = new List<Foo>()
{
new Foo() { Type = "a", Value = "aaaa" },
new Foo() { Type = "a", Value = "bbbb" },
new Foo() { Type = "b", Value = "cccc" },
new Foo() { Type = "d", Value = "dddd" },
new Foo() { Type = "d", Value = "eeee" },
new Foo() { Type = "d", Value = "ffff" },
new Foo() { Type = "a", Value = "gggg" },
new Foo() { Type = "b", Value = "hhhh" },
new Foo() { Type = "b", Value = "iiii" },
new Foo() { Type = "b", Value = "jjjj" }
};
var groups = list.GroupConsecutive((a, b) => a.Type == b.Type);
foreach (var group in groups)
{
Console.WriteLine("List " + group.Key);
foreach (var item in group)
{
Console.WriteLine("Type=" + item.Type + " Value=" + item.Value);
}
Console.WriteLine();
}
结果看起来像这样:
List 0
Type=a Value=aaaa
Type=a Value=bbbb
List 1
Type=b Value=cccc
List 2
Type=d Value=dddd
Type=d Value=eeee
Type=d Value=ffff
List 3
Type=a Value=gggg
List 4
Type=b Value=hhhh
Type=b Value=iiii
Type=b Value=jjjj
答案 1 :(得分:3)
这可以推广或制作扩展方法,但你明白了:
public static IEnumerable<List<Item>> GroupConsecutive(IEnumerable<Item> items)
{
if (items.Any())
{
string firstType = items.Select(i => i.Type).First();
var adjacents = items.TakeWhile(i => i.Type == firstType).ToList();
yield return adjacents;
foreach (var group in GroupConsecutive(items.Skip(adjacents.Count)))
{
yield return group;
}
}
}
使用此课程:
public class Item
{
public string Type { get; set; }
public string Value { get; set; }
}
编辑:以下是此解决方案的权衡:
优点:
缺点:
items
。如果items
是List,那么这不是什么大问题,但如果items
是一个IEnumerable,它为每个项执行昂贵的计算,则此方法可能比其他方法慢。如果您希望使用延迟评估对items
进行一次迭代,我建议使用GroupAdjacent回答中提到的this扩展方法或使用yield return
进行循环。如果你想要一个没有延迟评估的迭代,我建议使用循环或Aggregate
方法。
答案 2 :(得分:2)
这是使用Aggregate
的唯一LINQ答案。
我从这开始:
var list = new []
{
new { type="a", value="aaaa", },
new { type="a", value="bbbb", },
new { type="b", value="cccc", },
new { type="d", value="dddd", },
new { type="d", value="eeee", },
new { type="d", value="ffff", },
new { type="a", value="gggg", },
new { type="b", value="hhhh", },
new { type="b", value="iiii", },
new { type="b", value="jjjj", },
};
然后这样做了:
var accumulator = new List<KeyValuePair<string, List<string>>>()
{
new KeyValuePair<string, List<string>>(
list.First().type,
new List<string>()),
};
var results = list.Aggregate(accumulator, (a, x) =>
{
if (a.Last().Key == x.type)
{
a[a.Count - 1].Value.Add(x.value);
}
else
{
a.Add(new KeyValuePair<string, List<string>>(
x.type,
new List<string>(new [] { x.value, })));
}
return a;
});
results
然后如下所示:
请告诉我这是否适合您。
答案 3 :(得分:2)
没有LINQ,普通的旧c#以及每个初级开发人员都希望遵循的单循环。更不用说更快了。
public class Foo
{
public string Type { get; set; }
public string Value { get; set; }
}
List<Foo> list = new List<Foo>()
{
new Foo() { Type = "a", Value = "aaaa" },
new Foo() { Type = "a", Value = "bbbb" },
new Foo() { Type = "b", Value = "cccc" },
new Foo() { Type = "d", Value = "dddd" },
new Foo() { Type = "d", Value = "eeee" },
new Foo() { Type = "d", Value = "ffff" },
new Foo() { Type = "a", Value = "gggg" },
new Foo() { Type = "b", Value = "hhhh" },
new Foo() { Type = "b", Value = "iiii" },
new Foo() { Type = "b", Value = "jjjj" }
};
Foo previous = null;
List<List<Foo>> separateLists = new List<List<Foo>>();
List<Foo> currentList = null;
foreach (var foo in list)
{
if (null == previous || previous.Type != foo.Type)
{
currentList = new List<Foo>();
separateLists.Add(currentList);
}
currentList.Add(foo);
previous = foo;
}