You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Linq实现值变化时将项拆分至不同分组

问题描述

我想用Linq对列表项进行分组,要求每当Command类型发生变化时,之前连续的相同Command项归入一个单独分组。

尝试用GroupBy方法,但它会把所有相同Command的项归为一组,完全忽略了顺序。我的代码如下:

var groups =  data
    .GroupBy(step => step.Command)
    .Select(g => new KeyValuePair<Command, IEnumerable<MyClass>>(g.Key, g.ToList()))
    .ToList();

数据示例

var data = new List<MyClass>
{
    new MyClass() { Index = 0, Command = Command.M },
    new MyClass() { Index = 0, Command = Command.M },
    new MyClass() { Index = 0, Command = Command.M },
    new MyClass() { Index = 1, Command = Command.M },
    new MyClass() { Index = 1, Command = Command.M },
    new MyClass() { Index = 1, Command = Command.RC },
    new MyClass() { Index = 1, Command = Command.RC },
    new MyClass() { Index = 2, Command = Command.RC },
    new MyClass() { Index = 2, Command = Command.M },
    new MyClass() { Index = 2, Command = Command.M },
    new MyClass() { Index = 2, Command = Command.RC },
    new MyClass() { Index = 2, Command = Command.M },
    new MyClass() { Index = 3, Command = Command.M },
    new MyClass() { Index = 4, Command = Command.RC },
};

当前实际输出

Group:
  Index: 0, Command: M
  Index: 0, Command: M
  Index: 0, Command: M
  Index: 1, Command: M
  Index: 1, Command: M
  Index: 2, Command: M
  Index: 2, Command: M
  Index: 2, Command: M
  Index: 3, Command: M
Group:
  Index: 1, Command: RC
  Index: 1, Command: RC
  Index: 2, Command: RC
  Index: 2, Command: RC
  Index: 4, Command: RC

期望输出

Group:
  Index: 0, Command: M
  Index: 0, Command: M
  Index: 0, Command: M
  Index: 1, Command: M
  Index: 1, Command: M
Group:
  Index: 1, Command: RC
  Index: 1, Command: RC
  Index: 2, Command: RC
Group:
  Index: 2, Command: M
  Index: 2, Command: M
Group:
  Index: 2, Command: RC
Group:
  Index: 2, Command: M
  Index: 3, Command: M
Group:
  Index: 4, Command: RC

相关类定义

public enum Command
{
    M,
    RC
}
public class MyClass
{
    public Command Command { get; set; }
    public int Index { get; set; }
}
解决方案

GroupBy是基于键值的全局分组,无法处理连续相同键的场景,需要手动跟踪分组的变化。以下是两种可行实现:

方式一:直接用Aggregate累积分组

通过Aggregate逐步构建分组,每次检查当前项的Command是否与最后一个分组的Command一致,不一致则新建分组:

var groups = data.Aggregate(new List<List<MyClass>>(), (list, item) =>
{
    if (list.Count == 0 || list.Last().First().Command != item.Command)
    {
        list.Add(new List<MyClass>());
    }
    list.Last().Add(item);
    return list;
})
.Select(g => new KeyValuePair<Command, IEnumerable<MyClass>>(g.First().Command, g))
.ToList();

方式二:自定义扩展方法(复用性更强)

如果需要多次使用连续分组逻辑,可以封装成扩展方法:

public static class EnumerableExtensions
{
    public static IEnumerable<IGrouping<TKey, TElement>> GroupByContiguous<TElement, TKey>(
        this IEnumerable<TElement> source,
        Func<TElement, TKey> keySelector,
        IEqualityComparer<TKey> comparer = null)
    {
        comparer ??= EqualityComparer<TKey>.Default;
        using var enumerator = source.GetEnumerator();
        if (!enumerator.MoveNext()) yield break;

        var currentKey = keySelector(enumerator.Current);
        var currentGroup = new List<TElement> { enumerator.Current };

        while (enumerator.MoveNext())
        {
            var item = enumerator.Current;
            var key = keySelector(item);
            if (comparer.Equals(currentKey, key))
            {
                currentGroup.Add(item);
            }
            else
            {
                yield return new Grouping<TKey, TElement>(currentKey, currentGroup);
                currentKey = key;
                currentGroup = new List<TElement> { item };
            }
        }

        // 返回最后一个分组
        yield return new Grouping<TKey, TElement>(currentKey, currentGroup);
    }

    // 辅助类实现IGrouping接口
    private class Grouping<TKey, TElement> : IGrouping<TKey, TElement>
    {
        public TKey Key { get; }
        private readonly IEnumerable<TElement> _elements;

        public Grouping(TKey key, IEnumerable<TElement> elements)
        {
            Key = key;
            _elements = elements;
        }

        public IEnumerator<TElement> GetEnumerator() => _elements.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }
}

使用时直接调用扩展方法:

var groups = data.GroupByContiguous(x => x.Command)
    .Select(g => new KeyValuePair<Command, IEnumerable<MyClass>>(g.Key, g.ToList()))
    .ToList();

两种方式都能得到你期望的连续分组结果。

内容的提问来源于stack exchange,提问作者theateist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 17:53:12