我有一個類Event
有兩個屬性:「ID」和「ExpirationTime」。 我有一個列表有許多事件,其中一些具有相同的ID。 我想創建有效的 LINQ查詢,將通過ID區分事件,併爲每個ID保持具有最小ExpirationTime的事件。如何使用LINQ來區分列表?
謝謝!
我有一個類Event
有兩個屬性:「ID」和「ExpirationTime」。 我有一個列表有許多事件,其中一些具有相同的ID。 我想創建有效的 LINQ查詢,將通過ID區分事件,併爲每個ID保持具有最小ExpirationTime的事件。如何使用LINQ來區分列表?
謝謝!
分組是很容易的,但是做了有效的「MinBy」與標準的LINQ to Objects是略顯凌亂:
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));
我想這應該這樣做:
events.GroupBy(x => x.ID, (key, items) => items.First(y => y.ExpirationTime == items.Min(z => z.ExpirationTime)))
威爾集團通過ID,在items
選擇結果是該事件(其中items
代表所有具有相同ID的事件)與最小ExpirationTime
。
也不會顯着,因爲:1)如果產生了IEnumerable,所以你必須通過的SelectMany 2)在哪裏可以包括幾個事件具有相同的到期日期 – Andrey 2011-02-09 15:51:02
拉平凡(最小值)爲O(n^2) – 2011-02-09 16:00:15
你是對的,但`First`也應該有效。 – 2011-02-09 16:01:24
events.GroupBy(e => e.ID).Select(g => new { ID = g.Key, Time = g.Min(e => e.ExpirationTime) });
這不會返回事件。 – 2011-02-09 15:59:17
LINQ's Distinct() on a particular property
簡單!你想分組他們並從組中選出一個優勝者。
List<Event> distinctEvents = allEvents
.GroupBy(e => e.Id)
.Select(g => g.OrderBy(e => e.ExpirationTime).First())
.ToList();
不錯!但請注意,排序是o(nlogn),而最大值是o(n) – 2011-02-09 15:53:55
@ohadsc您是對的。爲了便於使用/閱讀,我故意爲了一點點的表現而交易。另外 - 人們會期望每個組都比整個列表小很多,所以這些小型排序比排序整個列表要快。 – 2011-02-09 15:56:21
List<Event> events = null;
events
.GroupBy(e => e.ID)
.Select(g =>
g.First(e =>
e.ExpirationTime == g.Max(t =>
t.ExpirationTime
)
)
);
我相信這應該跑贏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;
}
}
說明:雖然這和the GroupBy
method proposed by Ani具有相同的算法複雜(據我無論如何,可以說),上述方法在實踐中更有效率有兩個原因。
GroupBy
內部使用一個Lookup<TKey, TValue>
(非常類似於Dictionary<TKey, List<TValue>>
)實際上填充與輸入序列的內容內部集合。這需要更多的內存,並且還具有性能影響,特別是由於這樣的事實:雖然子集合將具有O(1)插入時間,但它們偶爾需要調整它們自身的大小,這將是O(N)(其中N是子集合的大小)。這不是什麼大不了的事情,但是還是有很多工作需要你做需要。GroupBy
之前可以提供的枚舉(所以它的延遲執行,但隨後整個輸入序列需要之前被迭代遍歷GroupBy
的結果)。然後,您在Aggregate
的調用中重複遍歷每個組再次;所以總而言之,您將迭代輸入序列中的元素兩次,這比完成當前任務所需的次數多。正如我所說的,算法的複雜性是相同的,這意味着這兩種方法應該具有同等的可擴展性;這一個只是更快。我冒昧地測試了這兩種方法(主要是出於好奇),並發現上述方法大概在一半時間內執行,並導致比採用方法更少的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);
}
}
`用最小的ExpirationTime離開事件?`你是什麼意思? – 2011-02-09 15:44:20
他指保持,(法國adibe?) – Guillaume86 2011-02-09 15:46:56