簡體   English   中英

Linq - Group然后比較每個組中的元素

[英]Linq - Group then compare elements within each group

例如,假設在我的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和不同的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. 按ID分組
  2. 按日期排序
  3. 選擇每個組中項目[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函數:

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());
}

如果我正確理解您的要求和正確的工作代碼,您希望獲得所有更改。 由於您已經按ID訂購,因此您可以使用GroupBy獲取ID Group。 現在,您需要為Val -value從一個對象更改為另一個對象的每個ID-Group添加所有內容。 您可以使用以下單個查詢創建每個組的列表以通過索引訪問前一個元素,使用SelectMany來展平它們。

public IEnumerable<MyClass> GetUpdatedVals(List<MyClass> myVals, string groupName)
{
    return myVals
        .Where(v => v.GroupName == groupName)
        .OrderBy(v => v.Id)
        .ThenBy(v => v.Dt)
        .GroupBy(v => v.Id)
        .Select(g => g.ToList())
        .SelectMany(gList => gList
            .Where((v, idx) => idx == 0 || v.Val != gList[idx - 1].Val));
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM