How to distinguish a list using LINQ?

I have a class Eventthat has two properties: "ID" and "ExpirationTime". I have a list that contains many events, some of them have the same identifier. I want to create an efficient LINQ query that will distinguish events by identifier, and for each identifier this event will have the smallest ExpirationTime value.

Thank!

+3
source share
7 answers

Grouping is simple enough, but the efficient "MinBy" with standard LINQ to Objects is a bit confusing:

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

A cleaner using an operator such as MoreLinq . MinBy

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

LINQ's Distinct()

Simple! .

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

, 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;
    }
}

. GroupBy, Ani, ( , ), .

  • GroupBy Lookup<TKey, TValue> ( Dictionary<TKey, List<TValue>>), . , , - , , O (1), , O (N) ( N ). , .
  • № 1 , , , , GroupBy ( , GroupBy). Aggregate; , , .

, , , ; . ( - ) , GC ( ), GroupBy.

, , , , . , , , ( ); , .

+3

Assuming you can implement IComparable in your class Event(since LINQ Minhas no overload, returning the original element 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);
    }
}
+2
source

I think this should do it:

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

It will be grouped by identifier, choosing as the result the event in items(where it itemsrepresents all events with the same identifier) ​​with the smallest ExpirationTime.

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

Source: https://habr.com/ru/post/1790998/


All Articles