Bob Lotz
Bob Lotz

Reputation: 85

c# select instance from a list of classes based on property

Given the following class structure need to extract various members based on custom property value:

class Item{

    public Item(int position, Image image)
    {
        Position = position;
        Image = image;
    }

    public int Position { get; set; }
    public Image Image { get; set; }
    public int Quality { get; set; }
}

class Items : List<Item> {}
class ConsolidatedItems : List<Items> {}

How do I get a list of "best" Items based on Quality joined by Position (in other words group items where Position is the same):

Input

Items1:

Item1: 1, ImageBinary, 100
Item2: 2, ImageBinary, 98
Item3: 3, ImageBinary, 45
Item4: 4, ImageBinary, 66

Items2:
Item1: 1, ImageBinary, 76
Item2: 2, ImageBinary, 80
Item4: 4, ImageBinary, 33

Items3:
Item1: 2, ImageBinary, 76
Item2: 3, ImageBinary, 80
Item4: 4, ImageBinary, 90

Output

BestItems:
Item1: 1, ImageBinary, 100 (from Items1)
Item2: 2, ImageBinary, 98 (from Items1)
Item3: 3, ImageBinary, 80 (from Items3)
Item4: 4, ImageBinary, 90 (from Items4)

Upvotes: 0

Views: 2054

Answers (2)

Robert McKee
Robert McKee

Reputation: 21487

Assuming that the Position is already filled in, then you can use SelectMany to flatten your ConsolidatedItems, group them by the position, order the groups by position, and then choose the best quality from each group:

var bestItems = data
    .SelectMany(x => x)
    .GroupBy(x => x.Position)
    .OrderBy(x => x.Key)
    .Select(x => x.OrderByDescending(i=>i.Quality).First());

Upvotes: 0

Isolin
Isolin

Reputation: 876

LINQ

var best = data
    .GroupBy(x => x.Position)
    .Select(x => x.Aggregate(
        (result, item) => item.Quality > result.Quality ? item : result)
    );
  • assuming that data is a collection of Item
  • make sure to add using System.Linq;
  • best is an IEnumerable you can convert it to a list or array if you wish using ToList() or ToArray()

Edit based on the discussion: In case your data is an instance of ConsolidatedItems, i.e. a nested collection, you need to flatten it first using SelectMany:

var best = data
    .SelectMany(x => x)
    .GroupBy(x => x.Position)
    .Select(x => x.Aggregate(
        (result, item) => item.Quality > result.Quality ? item : result)
    );

Details

  1. data contains your entries
  2. GroupBy creates a collection with key being the property or computed value you selected for grouping. You can of course iterate over all its items which will come handy in the next steps.
  3. Select will compute a new entity for each group -- the one with the best quality.
  4. Aggregate is perhaps the most difficult function here. It iterates over the members of a group and keeps one element as the result. The result is initialized with the first element. Then Aggregate moves on over the whole collection and compares each item against the currently best result. At the end it returns the result with the best element.

Best Practice

Please note that your collection classes are not designed well. Have a look here why not to inherit form List.

Upvotes: 2

Related Questions