如何按多个 T 属性对 List<T> 进行排序?

55

假设我有一个歌曲列表。

Song {
    public string Name = "";
    public int PlayOrder = 0;
    }

现在我想首先按 PlayOrder(从零开始)排序,其次按名称字母顺序排序。

因此,排序后的结果示例将是(名称,播放顺序):

/*
    Pachelbel's Canon, 0
    A Happy Song, 4
    Beethoven's 5th, 4
    Some Other Song, 7
*/

你看到 PlayOrder = 4 的项是按字母顺序排序的吗?这正是我想要的。

目前我只按照一个字段进行排序:

List<Song> final = new List<Song>();

...

final.Sort((x, y) => x.PlayOrder.CompareTo(y.PlayOrder));

return final;

我该如何像上面演示的那样按名称排序?

3个回答

99
return final.OrderBy(s => s.PlayOrder).ThenBy(s => s.Name);

7
请注意,此操作返回一个新的IEnumerable<Song>序列,而不是就地对原始列表进行排序。 - LukeH
1
如果我想返回一个 List,我可以在末尾加上 .ToList() 吗? - MetaGuru
在您的另一条评论中,您提到OrderBy执行稳定排序,并描述了什么是稳定排序。您能否使用相同的示例解释不稳定排序? - MetaGuru
这里有一个很好的解释:https://dev59.com/fFDTa4cB1Zd3GeqPGyzg - Kris Ivanov
哦,使用THENBY - 当然了!!!我需要更经常地阅读文档 :) - Iofacture

48

如果你想继续使用 sort 方法,你需要让比较函数更加智能:

final.Sort((x, y) => {
    var ret = x.PlayOrder.CompareTo(y.PlayOrder);
    if (ret == 0) ret = x.Name.CompareTo(y.Name);
    return ret;
});

如果您想使用LINQ,那么可以选择K Ivanov发布的方法。


3
注意:Sort() 方法执行的是不稳定排序,而 OrderBy 方法执行的是稳定排序;也就是说,如果两个元素的键相等,则它们的顺序将被保留。 - Kris Ivanov
这是如何工作的?花括号之间发生了什么,我怎么能返回一个变量?它返回到哪里?花括号内的代码会运行一次还是多次? - MetaGuru
花括号中的代码是lambda表达式的主体,与您在问题中使用的lambda表达式相同。问题在于我需要超过1个语句才能完成这个操作,因此我必须在主体周围加上大括号。基本上,在那里的代码是比较代码,排序算法将多次调用它(数量级为:nlogn)。 - tster

0
如果您只有一种偏好的歌曲排序方式,您应该实现 IComparable 和/或 IComparable<Song>:
List<Song> songs = GetSongs();
songs.Sort(); // Sorts the current list with the Comparable logic

如果您有多种方法想要存储列表,IEqualityComparer<T> 是您想要实现的接口。然后,您可以在 List<T>Sort() 的参数中提供该比较器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接