Самый быстрый способ сортировки списка по двум полям, одно по алфавиту, а другое по выбору? - PullRequest
2 голосов
/ 30 января 2012

Допустим, у меня есть List<Pet>, и у каждого питомца есть поля Pet.Name и Pet.Type.

Например:

Имя: Боб Тип: Рыба

Имя: Рекс Тип: Собака

Имя: Альф Тип: Собака

Имя: Пушистый тип: Кошка

Имя: Аполлон Тип: Рыба

Имя: Манго Тип: Лошадь

Теперь я хочу отсортировать сначала по имени в алфавитном порядке, а затем по типу (не альфа).

Второй вид (Тип)не должен быть алфавитным, а только конкретным / нестандартным.

Например, допустим, порядок должен быть всегда: Рыба, Лошади, Собаки, Кошки.

Итак, правильно упорядоченный список изПриведенные выше данные будут выглядеть так:

Аполлон, Рыба

Альф, Собака

Боб, Рыба

Пушистый, Кошка

Манго, Лошадь

Рекс, Собака

Или, если быть более ясным:

А, Рыба

А, Лошадь

A, Собака

A, Кошка

B, Рыба

B, Лошадь

B, Собака

B, кошка

C, рыба

C, лошадь

C, собака

C, кошка

Что означает, что иногда кошка может появляться перед собакой, но только если есть кошки с именем 'A', но нет собак с именем 'A' ... понятно?

Так что они отсортированы по алфавиту, но затем в каждой группе букв она сортируется в соответствии с пользовательским способом на основе второго поля.

Итак, какой самый быстрый способ сортировки этого списка таким способом?

Ответы [ 2 ]

5 голосов
/ 30 января 2012

Как насчет:

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);
3 голосов
/ 30 января 2012

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

}

Если порядок типа питомца может измениться, то он может быть передан в Comparer в качестве параметра для построения, а не жестко закодирован.

HTH,
Алан.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...