Reputation: 87
I have a custom object Deck
that has a List<Card> cards
between its properties. Each Card
has its own List<string> colors
denoting the color of the card using one or more capital letters like such [W, U, B, R, G]
.
What I need to do is sort the cards
list based on the colors
list in a way that I get first all the cards from one color and so on for each color; with cards having more than one color I'd like them being sorted based on a custom priority list (like if it's W
and U
put it between W
cards) but I realize this is even more complex so it's not really a necessity for me.
What I tried to do is
deck.cards = deck.cards.OrderBy(x => x.colors).ToList();
but I receive an error stating that at least an object needs to implement ICompare.
What can I do to sort my deck list? Is it possible to not only sort it like described but also based on a specific order like B
before R
before G
and so on?
Upvotes: 0
Views: 410
Reputation: 42225
Based on the discussion in the comments, when a card has multiple colours, you want to select a single colour (the one that appears first in a priority list), and sort it on that basis.
// Higher-priority colours come first
var coloursPriority = new List<string>() { "W", "U", "B", "R", "G" };
// Turn the card's colour into an index. If the card has multiple colours,
// pick the smallest of the corresponding indexes.
cards.OrderBy(card => card.Colours.Select(colour => coloursPriority.IndexOf(colour)).Min());
Responding to the discussion in the comments: if you wanted to sort the cards based first on their highest-priority colour, and then by their next-highest-priority colour, etc, then this is a reasonably efficient way of doing it:
public class CardColourComparer : IComparer<List<int>>
{
public static readonly CardColourComparer Instance = new CardColourComparer();
private CardColourComparer() { }
public int Compare(List<int> x, List<int> y)
{
// Exercise for the reader: null handling
// For each list, compare elements. The lowest element wins
for (int i = 0; i < Math.Min(x.Count, y.Count); i++)
{
int result = x[i].CompareTo(y[i]);
if (result != 0)
{
return result;
}
}
// If we're here, then either both lists are identical, or one is shorter, but it
// has the same elements as the longer one.
// In this case, the shorter list wins
return x.Count.CompareTo(y.Count);
}
}
Then
// Higher-priority colours come first
var coloursPriority = new List<string>() { "W", "U", "B", "R", "G" };
cards.OrderBy(card =>
card.Colours.Select(colour => coloursPriority.IndexOf(colour)).OrderBy(x => x).ToList(),
CardColourComparer.Instance);
This takes advantage of the fact that OrderBy
applies the keySelector
delegate to each item once only. We use this to turn each card into a list containing the priority of each of its colours (higher priorities have lower values), ordered with the higher-priority ones first. We then sort these keys, using a custom comparer which compares two of these lists.
Note that this doesn't care about the order of the colours associated with each card: [W, U]
will sort the same as [U, W]
. To take the order into account (so [W]
comes before [W, U]
comes before [U, W]
, do this:
cards.OrderBy(card =>
card.Colours.Select(colour => coloursPriority.IndexOf(colour)).ToList(),
CardColourComparer.Instance);
Upvotes: 4
Reputation: 1053
You can get a list of ordered cards by using the Aggregate function as follows:
var result = deck.Cards
.OrderBy(x => x.Colours.Aggregate((total, part) => total + part.ToLower()))
.ToList();
This assumes that the cards with multiple colours have those in an ordered list.
e.g.
card1.Colours = new List<string>() { "W", "X" };
card2.Colours = new List<string>() { "W" };
card3.Colours = new List<string>() { "U" };
card4.Colours = new List<string>() { "U", "W" };
Will return the cards in the order:
"U", "UW", "W", "WX"
Upvotes: 1