C#中的自定义排序

时间:2017-03-21 21:11:52

标签: 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;
}

5 个答案:

答案 0 :(得分: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));

以上是上述版本的较长版本,可以更详细地解释发生了什么:

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

答案 1 :(得分:3)

@stakx答案的更新版本。嗯,我认为这应该是更好的解决方案,如果这些值是固定的,也可以在其他地方使用。枚举中的每个值都有其int值,通过它可以对它们进行排序。

public enum Names 
{
   KK, //0
   AB, //1
   BC, //2
   DD, //3
   FV, //4
   ER, //5
   PP, //6
   WW //7
}

var packageList = list.OrderBy(p => p.Name);

<强>更新

在类

中使用enum示例
public class Package 
{
   public Names Name { get; set; }
}

如果需要获取此枚举的字符串值,则只需使用此

Package package - 包类变量

package.Name.ToString();

如果需要整个枚举名称列表(枚举键名称),可以使用Enum类方法:

  

Enum.GetNames(Type enumType)返回包含所有已定义的枚举键名称的字符串数组。

Enum.GetNames(typeof(Names))

答案 2 :(得分:3)

你可以将它归结为两行(一行用于数组定义,一行用于排序):

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

//...

var list =  mail.Package.OrderBy(p => Array.IndexOf(PackageOrder, p.Name)).ToList();

但我们可以做得更好。

到目前为止,代码要么需要对引用数组进行几次O(n)查找,要么切换到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(); 

这保证了在排序过程中每个包只能进行一次查找。我们的想法是首先创建原始数据的投影,该投影还包括索引,然后按索引排序,最后投影回原始数据。现在唯一的问题是是否使用数组或字典,这主要取决于引用数组的大小(对于这个大小数据棒与数组,超过大约15项,切换到字典;但它取决于你的类型的GetHashCode()表现。)

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

最后,我怀疑这里需要一个实际的List<T>。只需将声明更改为var,然后删除最后的.ToList()即可。等待ToList()ToArray(),直到您完全需要它为止,然后使用简单的IEnumerable<T>。这通常可以大大提高性能。

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

答案 3 :(得分:2)

替代..(我还没有编译)

var indexPositions = new Dictionary<string, int> {
    { "KK", 0 },
    { "AB", 1 },
    { "BC", 2 },
    { "DD", 3 },
    { "FV", 4 },
    { "ER", 5 },
    { "PP", 6 },
    { "WW", 7 }
}
foreach (var package in mail.Package)
{   // access position
    int index;
    if (!indexPositions.TryGetValue(a.Name, out index)) {
        throw new KeyNotFoundException()
    }
    list[index] = package;
}

答案 4 :(得分:1)

除了通常的OrderBy Array.IndexOf方法外,该列表还可以就地排序:

string[] order = { "KK", "AB", "BC", "DD", "FV", "ER", "PP", "WW" };

list.Sort((a, b) => Array.IndexOf(order, a.Name).CompareTo(Array.IndexOf(order, b.Name)));

更高级O(n)替代方案:

var lookup = list.ToLookup(x => x.Name);

list = order.SelectMany(x => lookup[x]).ToList();