Как я могу заставить OrderBy или IComparer сортировать мой конкретный список?

Словарь ints, который я изначально сгенерировал случайным образом в диапазоне от 1 до 100, используя:

var lstNumbers = Enumerable.Range(1, 100).OrderBy(n => Guid.NewGuid)
                           .GetHashCode()).ToList();

Словарь создается и используется для присвоения ColorType значений каждому из чисел в последовательности Red, Yellow, White, Red, Yellow, White и т. д. и т. д. всем числам в списке:

var startColor = ColorType.Red;
var map = new Dictionary<int, ColorType>();
foreach(var number in lstNumbers) {
  // First color for assignment
  map[number] = startColor.Next();
  // Go to the next color
  startColor = startColor.Next();
}

Затем я удаляю элементы несколькими способами:

foreach(KeyValuePair<int, ColorType> entry in map.ToList()) {
  if (entry.Key % 2 == 0 && entry.Value == ColorType.Red) {
    map.Remove(entry.Key);
  }
  if (entry.Key % 2 == 1 && entry.Value == ColorType.Yellow) {
    map.Remove(entry.Key);
  }
  if (entry.Key % 3 == 0 && entry.Value == ColorType.White) {
    map.Remove(entry.Key);
  }
}

Затем я сортирую числа в порядке возрастания:

foreach(var number in map.OrderBy(i => i.Key)) {
  Console.WriteLine(number);
}

Console.ReadLine();

Итак, мой список сейчас выглядит примерно так:

[53, Red]
[54, Yellow]
[55, White]
[56, Yellow]
[61, White]
[62, White]
[64, Yellow]
[65, Red]
ect., ect.

Теперь мне нужно отсортировать окончательный список из этого словаря по значению, чтобы белые результаты были вверху, желтые — в середине, а красные — внизу. Красный ‹ Желтый ‹ Белый, затем отобразите.

Я попробовал этот OrderBy, но, возможно, я неправильно его реализовал или он может не работать для моего сценария, потому что я получаю сообщение об ошибке:

Оператор == нельзя применять к операндам «ColorType» и «string».

foreach(var color in map.OrderBy(c => c.Value == "White" ? 0 : c.Value == "Yellow" ? 1 : 2)) {
  Console.WriteLine(color);
}

Я пытался использовать IComparer, но столкнулся с проблемой, потому что я использую пользовательский тип в своем словаре, который я использую для цветов:

public class CustomerComparer : IComparer<KeyValuePair<int, ColorType>> {
  private List<string> orderedColors = new List<string>() { "White", "Yellow", "Red" };

  public int Compare(KeyValuePair<int, ColorType> str1, KeyValuePair<int, ColorType> str2) {
    return orderedColors.IndexOf(str1.Value) - orderedColors.IndexOf(str2.Value);
  }
}

var sorted = map.OrderBy(x => x, new CustomerComparer());

foreach(var entry in sorted) {
  Console.WriteLine("{0}: {1}", entry.Key, entry.Value);
}
Console.ReadKey();

Я предполагаю, что есть лучший способ сделать это, по-другому, или я что-то упускаю полностью.


person Justeasy StackAttacka    schedule 07.04.2018    source источник
comment
А как насчет colors.OrderBy(c => c.Value != ColorType.White).ThenBy(c => c.Value != ColorType.Yellow).ThenBy(c => c.Value != ColorType.Red)   -  person    schedule 08.04.2018
comment
Который 1_? ? Он существует в нескольких библиотеках. Предоставьте полную квалификацию для этого.   -  person Racil Hilan    schedule 08.04.2018
comment
Я бы использовал один int16 для вашего ключа словаря. Наименее значащие 7 бит будут числом от 0 до 100. Биты 8 и 9 будут двухбитным цветовым кодом, где белый будет: 00, желтый = 01, красный будет 10. Тогда, когда вы сортируете в обратном порядке, красный будет первым , затем желтый и, наконец, белый.   -  person jdweng    schedule 08.04.2018
comment
@RacilHilan ColorType — это настраиваемый тип данных.   -  person Justeasy StackAttacka    schedule 08.04.2018
comment
Никогда не используйте руководство как источник случайности; не гарантировано, что он будет случайным; он гарантированно будет уникальным, и это разные свойства. Во-вторых, никогда не используйте хеш-код ни для чего, кроме балансировки хеш-таблицы. У вас есть две плохие практики в одном и том же выражении. Если вы хотите перетасовать список, то либо выполните сортировку по чему-то гарантированно случайному, либо реализуйте перетасовку.   -  person Eric Lippert    schedule 08.04.2018
comment
@ Эрик Да, спасибо, я знаю, что это скорее перетасовка, чем случайность.   -  person Justeasy StackAttacka    schedule 08.04.2018
comment
@ Эрик Любые предложения каких-либо статей или ссылок, в которых описывается, как гарантировать случайность при перетасовке ключей словаря?   -  person Justeasy StackAttacka    schedule 08.04.2018
comment
@JusteasyStackAtacka: Может быть, просто прочитайте stackoverflow.com/q/375351/18192? Или просто используйте класс RandomSubset Леопольда Бушкина (из MoreLinq Джона Скита). библиотека). Задокументировано, что результаты выдаются в случайном порядке, поэтому просто установите size равным длине вашего списка.   -  person Brian    schedule 09.04.2018
comment
Если вам нужна последовательность псевдослучайных чисел, просто напишите static IEnumerable<double> PseudorandomNumbers() { Random r = new Random(); while(true) yield return r.NextDouble(); }, и теперь вы можете сказать Enumerable.Range(1,100).Zip(PseudorandomNumbers(), (i, d) => new { i, d }).OrderBy(p=>p.d).Select(p=>p.i).ToList(), и все готово. Если вам нужна случайность криптостойкости, сделайте то же самое, но с бесконечной последовательностью чисел криптостойкости.   -  person Eric Lippert    schedule 09.04.2018


Ответы (2)


Как указывает ошибка, которую вы получаете, вы пытались сравнить ColorType с String. Вам нужно сделать две стороны одинаковыми, либо обе ColorType:

foreach (var color in map.OrderBy(c => c.Value == ColorType.White ?
                                       0 : c.Value == ColorType.Yellow ? 1 : 2)) {
    Console.WriteLine(color);
}

или оба String. Вы сказали, что ColorType был настраиваемым типом данных, поэтому я не знаю, вернет ли ToString() название цвета:

foreach (var color in map.OrderBy(c => c.Value.ToString() == "White" ?
                                       0 : c.Value.ToString() == "Yellow" ? 1 : 2)) {
    Console.WriteLine(color);
}

Например, если вы используете класс System.Drawing.Color, ToString() не будет работать, потому что он возвращает что-то вроде Color [Red]. Вместо этого вы можете использовать свойство Name:

foreach (var color in map.OrderBy(c => c.Value.Name == "White" ?
                                       0 : c.Value.Name == "Yellow" ? 1 : 2)) {
    Console.WriteLine(color);
}

Поэтому я не знаю, есть ли в вашем классе ColorType аналогичное свойство, которое возвращает название цвета, или ToString() выполнит эту работу.

person Racil Hilan    schedule 07.04.2018
comment
Вау, хорошо, да, я вижу это в первом блоке кода. Я не должен был использовать строки. Спасибо что подметил это! Теперь мне просто нужно разделить считывание двух строк записи, чтобы их разделяла линия. Я думал, что break; сработает, но это не так. - person Justeasy StackAttacka; 08.04.2018

Я не думаю, что вам нужен пользовательский компаратор. Вы можете объединять заказы с .ThenBy.

Ты можешь написать

 var sorted = map.OrderBy(c => c.Value != ColorType.White)
                .ThenBy(c => c.Value != ColorType.Yellow)
                .ThenBy(c => c.Value != ColorType.Red);

Обратите внимание на оператор != — вы сортируете по логическим значениям, поэтому false предшествует true.

person Community    schedule 07.04.2018
comment
Это хорошее решение для нескольких строк, и я проголосовал за него. Я должен отметить, однако, что если список огромен, мы, вероятно, могли бы сделать его более эффективным, сделав начальное значение ключа естественной сортировкой на основе его значения с битами, как упоминал @jdweng - person angelsix; 08.04.2018