Reputation: 10460
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
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
Reputation: 111
var result = persons.Select(person => new { person, maxDate = new [] { person.Activity1, person.Activity2, person.Activity3 }.Max() });
Upvotes: 0
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
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