C#中的自定义排序

cas*_*las 0 c#

有一个Package项目列表按排序GUID,但我需要按如下顺序排序

KK %, AB, AB art, DD %, FV, ER, PP and WW

我已经实现如下,但我想知道有更好的方法吗?

List<PackageType> list = new List<PackageType> (8);           
foreach (var a in mail.Package)
{
 if (a.Name == "KK %")
    list[0] = a;
 else if (a.Name == "AB art")
    list[1] = a;
 else if (a.Name == "AB")
    list[2] = a;
 else if (a.Name == "DD %")
    list[3] = a;
 else if (a.Name == "FV")
    list[4] = a;
 else if (a.Name == "ER")
    list[5] = a;
 else if (a.Name == "PP")
    list[6] = a;
 else if (a.Name == "WW")
   list[7] = a;
}
Run Code Online (Sandbox Code Playgroud)

Joe*_*orn 8

您可以将其缩减为两行(一行用于数组定义,另一行用于排序):

var PackageOrder = new[] { "KK %", "AB", "AB art", "DD %", "FV", "ER", "PP", "WW"};

//...

var list =  mail.Package.OrderBy(p => Array.IndexOf(PackageOrder, p.Name)).ToList();
Run Code Online (Sandbox Code Playgroud)

但我们还可以做得更好。

到目前为止的代码要么需要在引用数组中进行多次 O(n) 查找,要么切换到 a Dictionary<string,int>,对于1可能与任务不成比例的值,每次查找都是 O(1) 。在排序操作过程中,每个包项可能需要多次这样的查找,这意味着这可能比您想要的效率低。

我们可以这样解决:

private static string[] Names = new[] { "KK", "AB", "BC", "DD", "FV", "ER", "PP", "WW" };

//...

var list =  mail.Package.
           Select(p => new {Package = p, Index = Array.IndexOf(Names, p.Name)}).
           OrderBy(p => p.Index).
           Select(p => p.Package).ToList(); 
Run Code Online (Sandbox Code Playgroud)

这保证在排序过程中每个包只查找一次。这个想法是首先创建一个包含索引的原始数据的投影,然后按索引排序,最后投影回原始数据。现在唯一的问题是是使用数组还是字典,这主要取决于引用数组的大小(对于这个大小数据粘数组,对于超过15个项目,切换到字典;但它取决于GetHashCode()您类型的性能)。

当然,还有 YAGNI 需要考虑。对于大集合,这通常会好得多,但对于小数据,它可能不值得,或者如果数据碰巧以某种幸运的方式排序,它会使事情变得更慢。如果您受到内存压力的限制而不是 CPU 时间(在 Web 服务器上很常见),它也会使事情变慢。但从一般意义上讲,这是朝着正确方向迈出的一步。

最后,我质疑List<T>这里是否需要实际。只需将声明更改为var并删除.ToList()末尾的 。等待电话ToList()ToArray()直到您绝对需要它,IEnumerable<T>然后再使用简单的方法。这通常可以大大提高性能。

在这种情况下(作为参考,我稍后添加了这一段),看起来您总共只有 8 个项目,这意味着额外的代码并没有真正为您节省任何东西。考虑到这一点,我只会坚持使用此答案顶部的两行解决方案(当性能无关紧要时,请使用更少或更简单的代码)。


sta*_*ica 7

// List<PackageType> list = ...;
var names = new[] { "KK", "AB", "BC", "DD", "FV", "ER", "PP", "WW" };
var sortedList = list.OrderBy(packageType => Array.IndexOf(names, packageType.Name));
Run Code Online (Sandbox Code Playgroud)

这是上面的更长版本,它更详细地解释了正在发生的事情:

// this array contains all recognized keys in the desired order;
private static string[] Names = new[] { "KK", "AB", "BC", "DD", "FV", "ER", "PP", "WW" };

// this helper method will return the index of a `PackageType`'s `Name`
// in the above array, and thus a key by which you can sort `PackageType`s.
static int GetSortingKey(PackageType packageType)
{
    var sortingKey = Array.IndexOf(Names, packageType.Name);
    if (sortingKey == -1) throw new KeyNotFoundException();
    return sortingKey;
}

// this is how you could then sort your `PackageType` objects:
// List<PackageType> list = ...;
IEnumerable<PackageType> sortedList = list.OrderBy(GetSortingKey);
Run Code Online (Sandbox Code Playgroud)

  • `OrderBy(packageType => SortingKeyFor(packageType))`可以压缩为`OrderBy(SortingKeyFor)` (3认同)
  • 或者:`list.OrderBy(x => Array.IndexOf(x))` (3认同)