【问题标题】:Sorting a List<T> based on T's List<string> property根据 T 的 List<string> 属性对 List<T> 进行排序
【发布时间】:2019-07-02 14:15:17
【问题描述】:

我有一个自定义对象Deck,它的属性之间有一个List&lt;Card&gt; cards。每个Card 都有自己的List&lt;string&gt; colors,用一个或多个大写字母表示卡片的颜色,例如[W, U, B, R, G]

我需要做的是根据colors 列表对cards 列表进行排序,首先我从一种颜色中获取所有卡片,依此类推;对于具有不止一种颜色的卡片,我希望它们根据自定义优先级列表进行排序(例如,如果它是 WU 将其放在 W 卡片之间),但我意识到这更加复杂,所以它不是对我来说真的是必需品。

我试图做的是

deck.cards = deck.cards.OrderBy(x => x.colors).ToList();

但我收到一条错误消息,指出至少有一个对象需要实现 ICompare。

我可以做些什么来对我的套牌列表进行排序?是否可以不仅像描述的那样对它进行排序,而且还可以基于特定的顺序,例如 B 之前 R 之前 G 等等?

【问题讨论】:

  • 我不遵循这句话:“(就像如果它是 W 和 U 放在 W 卡之间)。”一些示例输入和输出可能会有所帮助。
  • @canton7 每张卡片可以是一种或多种颜色,颜色用大写字母表示。如果可能的话,在对多色卡片进行排序时,我希望它们根据自定义的“优先级”顺序插入特定颜色的卡片之间,所以如果一张卡片同时是 W 和 U,我希望它在W卡。我意识到这让整个事情变得更加复杂,所以这不是必需品,更像是一个怪癖。
  • 你可以创建一个class 实现IComparer&lt;Card&gt; 并使用List.Sort 方法。
  • 我认为您需要更好地指定多色行为。你有“如果它的 W 和 U 把它放在 W only 和 U only 之间”但是如果你有第三种颜色 - 例如你有“B”、“BU”和“BW”。我假设 B 先去然后 BW,但是 BU 去哪里呢?或者它只是像你先用单色按字母顺序排序,然后在它们的末尾用多色卡片排序,如果合适的话,它们按第二种(或第三种等)颜色排序?我发现,通常一旦你清楚明确地解释了你希望你的排序如何工作,代码就很容易从中得到......
  • 话虽如此,我认为这可能已经足够复杂,您可能想要创建一个自定义比较器(即IComparer&lt;Card&gt;)。有一些与此相关的问题可能会给您一个良好的开端(例如 stackoverflow.com/questions/14336416/… )。

标签: c# list


【解决方案1】:

根据 cmets 中的讨论,当一张卡片有多种颜色时,您希望选择一种颜色(优先级列表中最先出现的颜色),并在此基础上对其进行排序。

// 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());

回应 cmets 中的讨论:如果您想首先根据卡片的最高优先级颜色对卡片进行排序,然后按其次高优先级颜色等排序,那么这是一种相当有效的方法:

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);
    }
}

然后

// 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);

这利用了OrderBykeySelector 委托仅应用于每个项目一次这一事实。我们使用它来将每张卡片变成一个列表,其中包含每种颜色的优先级(优先级越高,值越低),优先级高的先排序。然后,我们使用自定义比较器对其中两个列表进行比较,对这些键进行排序。

请注意,这并不关心与每张卡片相关的颜色顺序:[W, U] 将与 [U, W] 排序相同。要考虑顺序(所以[W][W, U] 之前在[U, W] 之前,请执行以下操作:

cards.OrderBy(card =>
    card.Colours.Select(colour => coloursPriority.IndexOf(colour)).ToList(),
    CardColourComparer.Instance);

【讨论】:

  • 我收到“IndexOf 方法没有重载需要 1 个参数”错误。
  • @nicktheone 已修复
  • 是的:dotnetfiddle.net/EmlEhx。显然,对于您的用例,您需要使用deck.cards = deck.cards.OrderBy(...).ToList(),就像您在问题中所做的那样。
  • @canton7:问题是“如果卡片有不止一种颜色,我希望它们根据自定义优先级列表进行排序(比如如果它是 W 和 U 放在 W 卡片之间)”我将其解释为“W 卡和 U 卡之间”的拼写错误。我会把它留给 OP 来评论他们是否关心这个。
  • 我认为您的更新答案现在很完美!它还考虑了没有颜色的可能性,这很棒!
【解决方案2】:

您可以使用Aggregate函数获取有序卡片列表,如下所示:

var result = deck.Cards
    .OrderBy(x => x.Colours.Aggregate((total, part) => total + part.ToLower()))
    .ToList();

这假设具有多种颜色的卡片在有序列表中。

例如

    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" };

将按顺序退回卡片:

“U”、“UW”、“W”、“WX”

【讨论】:

  • (注意这里没有考虑他的自定义颜色优先级列表)
  • 此外,由于所有这些临时字符串,使用 Aggregate 连接字符串非常昂贵。 string.Concat(x.Colours).ToLowerInvariant() 便宜很多,如果你甚至需要ToLower 电话。
猜你喜欢
  • 1970-01-01
  • 2020-11-18
  • 1970-01-01
  • 2021-05-25
  • 1970-01-01
  • 2017-01-12
  • 1970-01-01
  • 2010-10-17
  • 2020-08-03
相关资源
最近更新 更多