如果我有一些集合,例如:整数集合:
1 1 2 2 3 3 1 2 3 4 4 1 1 2 3
我只需要对后续元素进行分组,在此示例中为:
1 1
2 2
3 3
1
2
3
4 4
1 1
2
3
我构建了一个LINQ表达式,它创建了一个匿名集合,它在集合中有元素索引,它是相等标记,然后我加入集合,它向前移动了一个位置,然后是组元素。但我觉得这个算法太过分了。
有更优雅的解决方案吗?
答案 0 :(得分:3)
编辑:我找到了使用聚合扩展方法执行此操作的Linq方法。此方法允许您使用Func lambda中的accumulator参数查看集合中的先前项。这是:
static List<List<int>> Group2(List<int> data)
{
return data.Aggregate(new List<List<int>>(), (list, item) =>
{
if (list.Count == 0 || list[list.Count - 1][0] != item)
{
list.Add(new List<int> { item });
}
else
{
list[list.Count - 1].Add(item);
}
return list;
});
}
我无法想到Linq的方法,因为我不认为它的任何方法都允许你以这种方式查看以前的项目。这是一种通用的方式:
static IEnumerable<List<T>> Group<T>(IEnumerable<T> list, IEqualityComparer<T> comp)
{
T previous = default(T);
bool previousExists = false;
var eee = list.GetEnumerator();
List<T> result = null;
while(eee.MoveNext())
{
T current = eee.Current;
if (previousExists && comp.Equals(current, previous))
{
result.Add(current);
}
else
{
if (result != null)
yield return result;
result = new List<T> { current };
}
previous = current;
previousExists = true;
}
if (result != null)
yield return result;
}
答案 1 :(得分:1)
好吧,如果你真的不需要LINQ - 只需简单的循环即可完成任务:
var res = new List<List<int>>();
foreach(int i in data)
{
var c = res.Count;
if (c == 0 || res[c - 1][0] != i)
res.Add(new List<int>() { i });
else
res[c - 1].Add(i);
}
您也可以将linq与外部变量一起使用,但这可能更难阅读
答案 2 :(得分:1)
这是使用LINQ:
的实现//Input sequence
int[] input = new int[] { 1, 1, 2, 2, 3, 3, 1, 2, 3, 4, 4, 1, 1, 2, 3 };
//Group number
int i = 0;
//Result array [<group number>][]
int[][] values =
//Select new anonymous object, which contains the source value from input and its group number
input.Select((item, index) => new { Key = index > 0 ? (item == input[index - 1] ? i : ++i) : 0, Value = item })
//Group anonymous objects by group number
.GroupBy(pair => pair.Key)
//Select values for each group
.Select(g => g.Select(x => x.Value).ToArray())
.ToArray();
答案 3 :(得分:1)
问题是LINQ缺少Select
方法,可以让您点击上一个结果。
如果确实如此,你只需要写下这样的东西:
var sequences = items
.SelectWithPreviousResult(
new { Item = -1, GroupNumber = 0 }, // default result (used for first item)
(item, previous) => new
{
Item = item,
GroupNumber = previous.Item == x
? previous.GroupNumber
: previous.GroupNumber + 1 })
.GroupBy(x => x.GroupNumber, x => x.Item);
这样做是选择当前项目以及从0开始的GroupNumber
,仅在当前项目与上一项目不同时才增加。然后按GroupNumber
进行分组,并将组成员简化为项目。
当然,由于SelectWithPrevious
尚不存在,因此无法编译。但是,关于LINQ的好处是你可以很容易地编写自己的扩展方法。 SelectWithPreviousResult
方法可以像这样实现:
public static class LinqExtensions
{
public static IEnumerable<TResult> SelectWithPreviousResult<TSource, TResult>(
this IEnumerable<TSource> items,
TResult defaultResult,
Func<TSource, TResult, TResult> func)
{
var previousResult = defaultResult;
foreach (var item in items)
{
var result = func(item, previousResult);
previousResult = result;
yield return result;
}
}
}
以这种方式实现它的优点是你可以重用扩展方法来解决类似的问题,并且你的代码比使用纯循环或复杂的Aggregate
表达式获得了一些可读性。