I have a class like this
public class ValueGroup
{
public string Name { get; set; }
public List<Value> Values { get; set; }
}
public class Value
{
public int RealValue { get; set; }
public bool IsFavourite { get; set;
}
And a list of some items
var valueList = new List<ValueGroup>
{
new ValueGroup
{
Name = "1st Group",
Values = new List<Value>
{
new Value
{
RealValue = 6,
IsFavourite = false
},
new Value
{
RealValue = 2,
IsFavourite = true
},
new Value
{
RealValue = 4,
IsFavourite = false
}
}
},
new ValueGroup
{
Name = "2nd Group",
Values = new List<Value>
{
new Value
{
RealValue = 7,
IsFavourite = false
},
new Value
{
RealValue = 3,
IsFavourite = true
},
new Value
{
RealValue = 8,
IsFavourite = true
}
}
},
new ValueGroup
{
Name = "3rd Group",
Values = new List<Value>
{
new Value
{
RealValue = 9,
IsFavourite = false
},
new Value
{
RealValue = 1,
IsFavourite = true
},
new Value
{
RealValue = 5,
IsFavourite = false
}
}
}
}
So, now I would like to select the value group, which has the highest RealValue paired with an IsFavourit == true in its nested ValueList. So in this case, I want to select the 2nd group (the 3rd one has a bigger value, but this is not an isFavourite). Is there a chance to realize this with LINQ? Thanks for comments!
CodePudding user response:
If you want to select the whole ValueGroup
object, use MaxBy
.
ValueGroup? largestValueGroup = valueList
.Where(vg => vg.Values.Any(v => v.IsFavourite))
.MaxBy(vg => vg.Values
.Where(v => v.IsFavourite)
.Max(v => v.RealValue));
Console.WriteLine(largestValueGroup?.Name);
// 2nd Group
How this works:
The first
Where
only allowsValueGroups
through that have at least oneValues
entry whereIsFavourite
is true.MaxBy
starts to evaluate all the remaining groups. For each group:Filter out any
Value
which is not a favorite.Get the max of the resulting Value entries (remember, we're still inside of an individual group) and get the maximum value.
Normally, you'd get a
InvalidOperationException
if you pass an empty sequence toMax()
(such as if there were noValue
entries that were favorites), but that's what the very firstWhere
does (before theMaxBy
). When you runMax
, you know for certain that at least oneValue
entry is a favorite.
The
MaxBy
now has evaluated each sourceValueGroup
into a number, and finds the maximum, and returns the matching source object.However, if
MaxBy
got an empty collection, such is the case when ALLValueGroups
did not contain a favorite, thenMaxBy
itself will return null.
CodePudding user response:
So basically, all you have to do, is for every ValueGroup
extract the Highest Favorite RealValue while remembering the ValueGroup. Result: combinations of [Highest Favorite RealValue, ValueGroup]
From these combinations find the one with the Highest RealValue.
The problem with Max and MaxBy is that they don't work well with empty collections: what is the highest RealValue is all are not IsFavorite?
Therefore I'll use OrderBy and FirstOrDefault, so I'm always certain that I get a value, even if all are not IsFavorite
IEnumerable<ValueGroup> valueGroups = ...
ValueGroup valueGroupWithLargestFavoriteRealValue = valueGroups
.Select(valueGroup => new
{
HighestFavoriteRealValue = valueGroup.Values
.Where(value => value.IsFavorite)
.Select(value => value.RealValue)
.OrderByDescending(realValue => realValue)
.FirstOrDefault(),
ValueGroup = valueGroup,
})
.OrderByDescending(combination => combination.HighestFavoriteRealValue)
.Select(combination => combination.ValueGroup)
.FirstOrDefault();
In words: from every valueGroup
in your input sequence of valueGroups
make one combination element that has two properties:
- HighestFavoriteValue
- ValueGroup: the complete valueGroup
To calculate the HighesFavoriteValue of this valueGroup, take all Values of this valueGroup, and keep only those that are IsFavorite. From the remaining values take the RealValue. Result: a bunch of realValues that all has IsFavorite. Order this bunch in descending order and take the first or default one. If there were no IsFavorites you get zero, otherwise you have the Highest Favorite real value.
Order this sequence of combinations [HighestFavoriteValue, ValueGroup] by descending value of HighestFavoriteValue. From every combination in this sorted sequence select the ValueGroup. From the resulting sequence of ValueGroups take the first, or the default if there is no ValueGroup left.