OfirD
OfirD

Reputation: 10460

Linq query to return a data structure holding max DateTime property of each passed object

A function gets a list of Person objects. Each Person has a Guid property and multiple DateTime? properties.

I need a LINQ query to return any kind of data structure (I thought about a dictionary, but it is not mandatory) holding each Person object and its respective max DateTime? property.

So I have:

public class Person
{
    public Guid id {get; set;}
    public DateTime? Activity1 {get; set;}
    public DateTime? Activity2 {get; set;}
    public DateTime? Activity3 {get; set;}
}

And I need:

//{{ 1st Person's Guid, 1st Person's max DateTime}, { 2nd Person's Guid, 2nd Person's max DateTime}, ...}

Upvotes: 2

Views: 342

Answers (4)

bl4y.
bl4y.

Reputation: 540

I think you are looking for a one-line full LINQ solution.

Since you didn't mention that the number of DateTime? fields could vary, I hardcoded them into my solution.

However, if you wish to use a dynamic amount of DateTime? fields, I would advise you against reflection. Reflection is a last resort solution and you could solve the problem of the dynamic amount of DateTime? fields with a List<> or a Dictionary<>.

Let me know if you need a solution also for the dynamic amount of DateTime? fields.

List<Person> People = new List<Person>();
Dictionary<Person, DateTime?> MaxDateTime = People.ToDictionary(x => x, x => new[] { x.Activity1, x.Activity2, x.Activity3 }.Where(y => y.HasValue).DefaultIfEmpty().Max(y => new Nullable<DateTime>(y.Value)));

Edit: Dynamic solution

The best way to store a dynamic amount of DateTime objects is to add them to a List<>. You don't need individual properties within the class and also will not lose the indexation of the DateTime object, since objects are indexed in a List<> by default. You can also add them manually at the desired index.

With a List<> you can omit the Nullable<> type from your class. If an activity doesn't exist, you just simply won't add it to your List<>. When you get the Dictionary<> of the people and their maximum DateTime, it might occur that they don't have any activities. So in this case, a Nullable<> is needed to prevent the default value of the DateTime to be set.

public class Person
{
    public Guid Id { get; set; }
    public List<DateTime> Activities { get; set; }

    public void AddActivity(int Index, DateTime DateTime) => Activities.Insert(Index, DateTime);
}

public void Main()
{
    List<Person> People = new List<Person>();
    Dictionary<Person, DateTime?> MaxDateTimeOfPeople = People.ToDictionary(x => x, x => x.Activities.DefaultIfEmpty().Max(y => new Nullable<DateTime>(y)));
}

Upvotes: 1

Alex S
Alex S

Reputation: 111

var result = persons.Select(person => new { person, maxDate = new [] { person.Activity1, person.Activity2, person.Activity3 }.Max() });

Upvotes: 0

ocuenca
ocuenca

Reputation: 39326

I guess this is what you're looking for if the id is unique per each person:

var result=persons.ToDictionary(p=>p.id,
                                p=>new{
                                       Person=p,
                                       MaxDate=new[]{p.Activity1,
                                                     p.Activity2,
                                                     p.Activity3}.Max()});

You could convert the three properties into an array and get the max value using Linq.

Upvotes: 1

Marcello
Marcello

Reputation: 1289

As you don´t know what are the DateTime? properties, you would need to use reflection to loop through the properties of this type.

Also, you could return a list of Tuple<Person, DateTime?>. Here is the code:

public static List<Tuple<Person, DateTime?>> GetMax(List<Person> personList)
{
    List<Tuple<Person, DateTime?>> list = new List<Tuple<Person, DateTime?>>();

    foreach (Person p in personList)
    {
        DateTime? maxDate = null;

        var properties = p.GetType().GetProperties();
        foreach (var property in properties)
        {
            if (property.PropertyType == typeof(DateTime?))
            {
                DateTime? date = (DateTime?)property.GetValue(p);
                maxDate = Max(maxDate, date);
            }
        }

        list.Add(new Tuple<Person, DateTime?>(p, maxDate));
    }

    return list;
}

static DateTime? Max(DateTime? a, DateTime? b)
{
    if (!a.HasValue && !b.HasValue) return a;  // doesn't matter

    if (!a.HasValue) return b;
    if (!b.HasValue) return a;

    return a.Value > b.Value ? a : b;
}

Upvotes: 1

Related Questions