按两个字段(一个按字母顺序,另一个按自定义)对列表进行排序的最快方法

本文关键字:列表 方法 自定义 排序 顺序 字段 两个 一个 另一个 | 更新日期: 2023-09-27 18:22:05

假设我有一个List<Pet>,每个Pet有一个Pet.NamePet.Type字段。

例如:

名称:Bob类型:Fish

名称:雷克斯类型:狗

名称:Alf类型:狗

名称:蓬松型:Cat

名称:Apollo型号:Fish

名称:芒果类型:马

现在,我想先按名称的字母顺序进行排序,然后按类型(非alpha)进行排序。

第二种排序(Type)不是按字母顺序排列的,而是特定的/自定义的。

例如,假设顺序应该始终是:鱼、马、狗、猫。

因此,从上述数据中正确排序的列表如下所示:

Apollo,Fish

Alf,狗

Bob,Fish

蓬松,Cat

芒果、马

雷克斯,狗

或者更清楚地说:

A、 鱼类

A、 Horse

A、 狗

A、 Cat

B、 鱼类

B、 Horse

B、 狗

B、 Cat

C、 鱼类

C、 Horse

C、 狗

C、 Cat

这意味着有时猫可以出现在狗之前,但前提是有"A"名字的猫,但没有"A"名称的狗。。。明白了吗?

所以它是按字母顺序排序的,但在每个字母组中,它都是根据第二个字段按自定义方式排序的。

那么,以这种方式对列表进行排序的最快方法是什么呢?

按两个字段(一个按字母顺序,另一个按自定义)对列表进行排序的最快方法

怎么样:

string[] orderedTypes = { "Fish", "Horses", "Dogs", "Cats" };
var orderedPets = sourcePets.OrderBy(pet => pet.Name)
                            .ThenBy(pet => Array.IndexOf(orderedTypes, pet.Type));

现在,它的效率不如预期,因为它需要多次线性搜索orderedTypes字符串数组来执行二级排序,但只有4种宠物,这应该不会太糟糕。

如果你真的很担心(比如还有几种宠物类型),你可以先创建一个查找:

var orderByType = new[] { "Fish", "Horses", "Dogs", "Cats" }
                  .Select(Tuple.Create<string, int>)
                  .ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2);
var orderedPets = sourcePets.OrderBy(pet => pet.Name)
                            .ThenBy(pet => orderByType[pet.Type]);

如果你有一个像这样的枚举而不是字符串:

 public enum AnimalType
 {
     Fish, Horses, Dogs, Cats
 }

那么操作就简单到:

var orderedPets = sourcePets.OrderBy(pet => pet.Name)
                            .ThenBy(pet => pet.Type);

OrderBy()将使用自定义比较器执行此操作。

public class Pet {
    public string Name { get; set; }
    public string Type { get; set; }
}
[TestMethod]
public void TestMethod1() {
    var pets = new List<Pet>() {
            new Pet{Name= "Bob", Type= "Fish"},
            new Pet{Name= "Rex", Type= "Dog"},
            new Pet{Name= "Alf", Type= "Dog"},
            new Pet{Name= "Fluffy", Type= "Cat"},
            new Pet{Name= "Apollo", Type= "Fish"},
            new Pet{Name= "Mango", Type= "Horse"}
    };
    var expected = new List<string>() {
            "Apollo, Fish",
            "Alf, Dog",
            "Bob, Fish",
            "Fluffy, Cat",
            "Mango, Horse",
            "Rex, Dog",
    };

    var sortedPets = pets.OrderBy(pt => pt, new PetEqualityComparer()).Select(pt => string.Format("{0}, {1}", pt.Name, pt.Type));
    Assert.IsTrue(expected.SequenceEqual(sortedPets));
}
public class PetEqualityComparer : IComparer<Pet> {
    readonly static string[] PetTypes = new [] { "Fish", "Horse", "Dog", "Cat" };
    public int Compare(Pet x, Pet y) {
        var xFirst = string.IsNullOrEmpty(x.Name) ? 'A' - 1 : x.Name[0];
        var yFirst = string.IsNullOrEmpty(y.Name) ? 'A' - 1 : y.Name[0];
        if (xFirst != yFirst) {
                return xFirst.CompareTo(yFirst);
        }
        return Array.IndexOf(PetTypes,x.Type).CompareTo(Array.IndexOf(PetTypes, y.Type));                
    }
}

如果Pet-Type顺序可以更改,那么它可以作为构造时的参数传递到比较器中,而不是硬编码。

HTH,
阿兰。