我有一个类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