score:4
The grouping is easy enough, but doing an efficient "MinBy" with standard LINQ to Objects is slightly messy:
var lowestByID = items.GroupBy(x => x.ID)
.Select(group => group.Aggregate((best, next) =>
best.ExpirationTime < next.ExpirationTime
? best : next));
It's cleaner with a MinBy
operator, such as the one provided with MoreLinq.
var lowestByID = items.GroupBy(x => x.ID)
.Select(group => group.MinBy(x => x.ExpirationTime));
score:0
List<Event> events = null;
events
.GroupBy( e => e.ID )
.Select( g =>
g.First( e =>
e.ExpirationTime == g.Max( t =>
t.ExpirationTime
)
)
);
score:1
I think this should do it:
events.GroupBy(x => x.ID, (key, items) => items.First(y => y.ExpirationTime == items.Min(z => z.ExpirationTime)))
Will group by ID, selecting as the result the event in items
(where items
represents all the events with the same ID) with the smallest ExpirationTime
.
score:1
events.GroupBy(e => e.ID).Select(g => new { ID = g.Key, Time = g.Min(e => e.ExpirationTime) });
score:2
Assuming you can implement IComparable on your Event
class (since LINQ's Min
doesn't have an overload returning the original item otherwise), you can do:
var distinct = events.GroupBy(evt => evt.Id).Select(grp => grp.Min());
Example:
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);
}
}
score:3
LINQ's Distinct() on a particular property
Simple! You want to group them and pick a winner out of the group.
List<Event> distinctEvents = allEvents
.GroupBy(e => e.Id)
.Select(g => g.OrderBy(e => e.ExpirationTime).First())
.ToList();
score:3
I believe this should outperform the GroupBy
suggestion (see brief explanation below):
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;
}
}
Explanation: While this and the GroupBy
method proposed by Ani have the same algorithmic complexity (as far as I can tell, anyway), the above approach is more efficient in practice for two reasons.
GroupBy
internally uses aLookup<TKey, TValue>
(very similar to aDictionary<TKey, List<TValue>>
) which actually populates internal collections with the contents of the input sequence. This requires more memory and also has a performance impact, particularly due to the fact that while the sub-collections will have amortized O(1) insertion time, they will occasionally need to resize themselves, which will be O(N) (where N is the size of the sub-collection). This is not a big deal, but it's still a lot more work you really need to be doing.- A consequence of point #1 is that this in turn requires iterating over each element in the input sequence before
GroupBy
can provide an enumerator (so it's deferred execution, but then the entire input sequence needs to be iterated before iterating over the result ofGroupBy
). Then you're iterating over each group again in the call toAggregate
; so in all, you're iterating over the elements in the input sequence twice, which is more times than necessary to accomplish the task at hand.
As I said, the algorithmic complexity is the same, which means the two approaches should be equally scalable; this one is simply faster. I took the liberty of testing both approaches (out of curiosity, mostly) and found the above to execute in roughly half the time and cause fewer GC collections (a rough approximation of memory usage) than the GroupBy
approach.
These are minute concerns, which it would normally be a waste of time to think too much about. The only reason I mention them is that you asked for an efficient solution (and even bolded the term); so I figured you would want to take these kinds of factors into consideration.
Source: stackoverflow.com
Related Query
- How do I create a linq query that returns the distinct 3 letter prefix of words in a list using deferred execution
- How to write SQL translateable linq code that groups by one property and returns distinct list
- How to select values within a provided index range from a List using LINQ
- How to remove duplicates from collection using IEqualityComparer, LinQ Distinct
- How to get distinct instance from a list by Lambda or LINQ
- How to get distinct with highest value using Linq
- Select distinct values from a list using LINQ in C#
- How to update value in a List using LINQ
- How to update an element with a List using LINQ and C#
- C# How to split a List in two using LINQ
- How do I get list of id's as int using LINQ
- How to get Distinct Values from List(Of T) using Linq
- How to get all elements except the n'th element in a List using Linq
- How to reuse a linq expression for 'Where' when using multiple source tables
- How to convert list of objects with two fields to array with one of them using LINQ
- How to create a comma delimited string from distinct list of values using LINQ?
- How to use a Distinct on a column using Linq
- How to avoid redundant List using LINQ
- How to find the index of next matching element in list using LINQ
- How to get a distinct, case-insensitive list using Linq and Entity Framework
- How to generate a unique list of items from another list using LINQ in C#
- How to rename duplicates in list using LINQ
- How to fill a property of an object from another list using LINQ
- How can I write the following code more elegantly using LINQ query syntax?
- How to Convert Anonymous Type to List <dynamic> using Linq to SQL in C#
- Getting specific distinct record to a list using linq
- Using LINQ how do I create a List of one particular field of an entity from a collection entities
- How can I code an outer join using LINQ and EF6?
- How to filter list in asp.net/vb.net using linq
- How to add list of missing values in c# list using Linq
More Query from same tag
- Can I do a "Take(x)" on the "Include" entity in a Linq Query?
- Use listbox SelectedItems as LINQ argument
- C# XML document remove elements that fail schema - preferably split the file into valid and invalid
- Join list into subclass and list<subclass>
- Get dates that contain all selected products
- Dynamic columns in DataGridView control (ASP.NET)
- Splitting two sets
- NHibernate - LINQ Limitations
- How to enable $expand and $select on ODataQueryOptions?
- Formatting String from Data
- Do an Include on list derived on DBQuery
- Get custom object from lambda expression
- Searching on multiple strings
- Dictionary from other dictionary grouping by object value field using linq
- Filtering Data Table based on column values containing special charactors in asp.net c#
- What is the typeArguments parameter of the Expression.Call Method when making a Expression Tree?
- Emulating an SQL Query with Count of linked records using Linq
- Using LINQ on ObservableCollection with GroupBy and Sum aggregate
- Memory saving in GroupBy
- Is it possible to create something like a MethodCallExpression without knowing the types of the related expressions?
- Query entity and return back a dictionary
- Problems with passing Lambda LINQ statement as Func<>
- Linq 2 Sql Compare a local sequence to a query using 'Like' - most efficient method
- EF and Linq Except
- Count non null values in multiple columns with LINQ
- Return value of stored procedure is correct but column values are all NULL
- Find item from Viewbag.List
- Parent/Children Xml to DTO Object Model with LINQ
- Most Efficient Many-To-Many Linq Query
- C#: Chained Linq methods and casting