簡體   English   中英

如何使用LINQ區分列表?

[英]How to distinct a list using LINQ?

我有一個類Event ,它有兩個屬性:“ID”和“ExpirationTime”。 我有一個包含許多事件的列表,其中一些具有相同的ID。 我想創建一個有效的 LINQ查詢,它將通過ID區分事件,並且對於每個ID,保持事件具有最小的ExpirationTime。

謝謝!

分組很簡單,但使用標准的LINQ to Objects執行高效的“MinBy”有點麻煩:

var lowestByID = items.GroupBy(x => x.ID)
                      .Select(group => group.Aggregate((best, next) =>
                                   best.ExpirationTime < next.ExpirationTime 
                                  ? best : next));

它的清潔與MinBy運營商,如提供與一個MoreLinq

var lowestByID = items.GroupBy(x => x.ID)
                      .Select(group => group.MinBy(x => x.ExpirationTime));

LINQ對特定屬性的Distinct()

簡單! 你想把它們分組並從小組中挑出一個勝利者。

List<Event> distinctEvents = allEvents
   .GroupBy(e => e.Id)
   .Select(g => g.OrderBy(e => e.ExpirationTime).First())
   .ToList(); 

我認為這應該優於GroupBy建議(見下面的簡要說明):

IEnumerable<Event> DistinctEvents(IEnumerable<Event> events)
{
    var dict = new Dictionary<int, Event>();

    foreach (Event e in events)
    {
        Event existing;
        if (!dict.TryGetValue(e.Id, out existing) || e.ExpirationTime < existing.ExpirationTime)
        {
            dict[e.Id] = e;
        }
    }

    foreach (Event e in dict.Values)
    {
        yield return e;
    }
}

說明 :雖然這和Ani提出的GroupBy方法具有相同的算法復雜度(據我所知,無論如何),上述方法在實踐中更有效,原因有兩個。

  1. GroupBy內部使用Lookup<TKey, TValue> (非常類似於Dictionary<TKey, List<TValue>> ),它實際上使用輸入序列的內容填充內部集合。 這需要更多的內存並且還會對性能產生影響,特別是由於雖然子集合將分攤 O(1)插入時間,但它們偶爾需要調整自身大小,這將是O(N)(其中N是子集合的大小)。 這不是什么大問題,但它仍然需要做很多工作。
  2. 點#1的結果是,這又要求 GroupBy提供枚舉器之前迭代輸入序列中的每個元素(因此它是延遲執行,但是在迭代GroupBy的結果之前需要迭代整個輸入序列) 。 然后,您在對Aggregate的調用中再次遍歷每個組; 總而言之,您在輸入序列中迭代兩次元素,這比完成手頭任務所需的次數多一倍。

正如我所說,算法的復雜性是相同的,這意味着兩種方法應該具有相同的可擴展性; 這個只是更快。 我冒昧地測試這兩種方法(主要是出於好奇心),發現上述操作大約有一半的時間,並且比GroupBy方法導致更少的GC集合(內存使用的粗略近似)。

這些是微小的問題,通常是浪費時間去思考太多。 我提到它們的唯一原因是你要求一個有效的解決方案(甚至加粗了這個術語); 所以我想你會想要考慮這些因素。

假設你可以在你的Event類上實現IComparable(因為LINQ的Min沒有重載返回原始項),你可以這樣做:

var distinct = events.GroupBy(evt => evt.Id).Select(grp => grp.Min());

例:

void Main()
{
    var events = new List<Event>
    {
        new Event(1, DateTime.Now),
        new Event(1, DateTime.Now.AddDays(1)),
        new Event(2, DateTime.Now.AddDays(2)),
        new Event(2, DateTime.Now.AddDays(-22)),
    };

    var distinct = events.GroupBy(evt => evt.Id).Select(grp => grp.Min());
}

public class Event : IComparable<Event>
{
    public Event(int id, DateTime exp)
    {
        Id = id;
        Expiration = exp;
    }
    public int Id {get; set;}
    public DateTime Expiration {get; set;}

    public int CompareTo(Event other)
    {
        return Expiration.CompareTo(other.Expiration);
    }
}

我認為應該這樣做:

events.GroupBy(x => x.ID, (key, items) => items.First(y => y.ExpirationTime == items.Min(z => z.ExpirationTime)))

將按ID分組,選擇具有最小ExpirationTime items的事件(其中items表示具有相同ID的所有事件)作為結果。

events.GroupBy(e => e.ID).Select(g => new { ID = g.Key, Time = g.Min(e => e.ExpirationTime) });
        List<Event> events = null;
        events
            .GroupBy( e => e.ID )
            .Select( g =>
                g.First( e => 
                    e.ExpirationTime == g.Max( t =>
                        t.ExpirationTime
                    )
                )
            );

暫無
暫無

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

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