c# - Linq - 分组然后比较每个组内的元素

标签 c# linq group-by

假设,例如,在我的 C# 代码中,我有 MyClass ,定义为:

public class MyClass
{
    public string GroupName;
    public DateTime Dt;
    public int Id;
    public string Val;
    .... other properties ....
}

假设我有以下 List<MyClass> (将其显示为表格,因为它似乎是描述内容的最简单方法):

GroupName:       Dt:             Id:        Val:
Group1           2016/01/01      1          Val1
Group1           2016/01/02      1          Val1
Group1           2016/01/03      1          Val1
Group1           2016/01/04      1          Val2
Group1           2016/01/05      1          Val3
Group1           2016/01/06      1          Val1
Group1           2016/01/07      1          Val1
Group1           2016/01/08      1          Val4
Group1           2016/01/09      1          Val4

显然,对于多个 GroupName 会发生同样的事情s和不同Id

我想从此列表中得到的是,对于任何命名组,每个第一个更改的值 - 因此 Group1 的输出会是:

Dt:             Id:        Val:
2016/01/01      1          Val1
2016/01/04      1          Val2
2016/01/05      1          Val3
2016/01/06      1          Val1
2016/01/08      1          Val4

换句话说,对于给定的 GroupName :

  1. 按编号分组
  2. 按日期订购
  3. 选择每个组中的任何项目,其中 item[index] != item[index-1]

所以,我得到了以下代码:

public IEnumerable<MyClass> GetUpdatedVals(List<MyClass> myVals, string groupName)
{
    var filteredVals = myVals.Where(v => v.GroupName == groupName).ToList();

    return filteredVals
        .OrderBy(v => v.Id)
        .ThenBy(v => v.Dt)
        .Where((v, idx) => idx == 0 || v.Id != filteredVals[idx - 1].Id || v.Val != filteredVals[idx - 1].Val)
        .Select(v => v);
}

但似乎应该有更好的方法通过 Linq 使用 GroupBy 或无需创建单独的持有列表的方法来执行此操作。

有什么想法吗?或者这是“非常好”/最好的方法?

谢谢!

最佳答案

如果你想要更优雅的东西,你可以使用 https://stackoverflow.com/a/4682163/6137718 中描述的 GroupAdjacent by 函数:

public static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> GroupAdjacentBy<T>(
        this IEnumerable<T> source, Func<T, T, bool> predicate)
    {
        using (var e = source.GetEnumerator())
        {
            if (e.MoveNext())
            {
                var list = new List<T> { e.Current };
                var pred = e.Current;
                while (e.MoveNext())
                {
                    if (predicate(pred, e.Current))
                    {
                        list.Add(e.Current);
                    }
                    else
                    {
                        yield return list;
                        list = new List<T> { e.Current };
                    }
                    pred = e.Current;
                }
                yield return list;
            }
        }
    }
}

在按 Id 和 Dt 排序后,我们可以使用它对具有相同 Val 的所有相邻元素进行分组。然后从每个组中,我们选择第一个,因为它代表最近的变化。更新后的代码看起来像这样:

public IEnumerable<MyClass> GetUpdatedVals(List<MyClass> myVals, string groupName)
{
    return myVals
        .Where(v => v.GroupName == groupName)
        .OrderBy(v => v.Id)
        .ThenBy(v => v.Dt)
        .GroupAdjacentBy((x, y) => x.Val == y.Val && x.Id == y.Id)
        .Select(g => g.First());
}

关于c# - Linq - 分组然后比较每个组内的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36455168/

相关文章:

c# - 重构函数以使其更通用

c# - 比较同一类的 2 个对象

c# - 在 C# 中使用 PropertyInfo 的奇怪问题

c# - 递归层次父子

c# - 如何在 C# 中连接两个数组?

Python DataFrame - 为具有分组列(至少两列)的数据框绘制条形图

c# - 检查对象是否是 T 的子类的扩展方法

c# - 使用 LINQ 对列表的所有元素应用谓词

ios - 尝试按一个属性分组并获取核心数据中每组中的实体数

SQL根据激活/停用记录回答: which customers were active in a given month,