#c# #linq #list #sorting
#c# #linq #Список #сортировка
Вопрос:
В настоящее время я сортирую список с помощью LINQ to objects, а затем выполняю ToList()
обработку результатов:
var SortedPossibleMoveLocations = (from PML in PossibleMoveLocations
orderby Randomiser.Next()
orderby IsSameType(PML) ? (_Owner[PML] as TileFlowing).UnitsWithin : 0
orderby PossibleMoveLocationOrdering(PML)
select PML).ToList();
Я хочу преобразовать это для выполнения сортировки на месте, я думаю, используя List<T>.Sort()
метод. Если бы я упорядочивал только по одной вещи, я бы знал, как это сделать, однако, поскольку я упорядочиваю по PossibleMoveLocationOrdering
(который возвращает int
), затем по IsSameType(PML) ? (_Owner[PML] as TileFlowing).UnitsWithin : 0
, который вычисляется как int
, затем по Randomiser.Next()
(который возвращает случайное значение int), я не знаю, как это сделать.
Вопрос: Как мне написать функцию сравнения (или есть лучший метод), чтобы выполнить скрытую сортировку запроса LINQ выше.
Комментарии:
1. Обратите внимание, что множественный порядок почти всегда неправильный — это не добавит
ThenBy
, поэтому фактически инвертирует приоритет сортировки. Это должен быть один порядок с несколькими выражениями2. Я понимаю, что я получу порядок, противоположный порядку x, y, z . (Хотя я написал это до того, как узнал о синтаксисе single orderby). Вот почему в тексте под кодом я уточняю, что имеет значение. Хотя спасибо за информацию.
Ответ №1:
Для начала, указание трех orderby
предложений — плохая идея — вместо этого укажите несколько порядков, просто разделяя их запятой.
Я также не в восторге от идеи использования Randomiser.Next()
для упорядочивания, но это в сторону.
Ваш запрос LINQ должен выглядеть следующим образом (на данный момент все еще с Randomiser
in):
var query = (from PML in PossibleMoveLocations
orderby PossibleMoveLocationOrdering(PML),
IsSameType(PML) ? (_Owner[PML] as TileFlowing).UnitsWithin : 0,
Randomiser.Next()
select PML).ToList();
Лично я бы просто использовал для этого точечную нотацию:
var query = PossibleMoveLocations
.OrderBy(pml => PossibleMoveLocationOrdering(PML))
.ThenBy(pml => IsSameType(pml) ?
(_Owner[pml] as TileFlowing).UnitsWithin : 0)
.ThenBy(pml => Randomiser.Next())
.ToList();
Для сортировки на месте вам в основном нужен Comparison<T>
or IComparer<T>
, который может тестировать несколько вещей, а также реализация, которая создает средство сравнения с использованием свойств. Вы можете сделать это вручную (согласно коду Marc), но так получилось, что у меня есть несколько вспомогательных классов и методов расширения в MiscUtil:
var comparer = ProjectionComparer<PossibleMove>
.Create(pml => PossibleMoveLocationOrdering(PML));
.ThenBy(pml => IsSameType(pml) ? ...)
.ThenBy(...);
list.Sort(comparer);
Обратите внимание, что использовать Randomizer
здесь определенно плохая идея, поскольку она будет вызываться при каждом сравнении (для объектов с равными первыми частями)… это может привести к непоследовательному сравнению, такому что x
< y
< z
< x
.
Комментарии:
1. Какой была бы альтернатива orderby по случайному числу в конечном случае (все остальные значения order by равны)?
2. Кроме того, использование нескольких orderby — плохая идея исключительно из-за удобства чтения или есть другая причина?
3. @George: В принципе, в этом случае у вас нет согласованного порядка. Если вы не можете последовательно различать два объекта, почему бы просто не возвращать 0 при их сравнении?
4. @George: Использование нескольких
orderby
предложений будет намного менее эффективным — это приведет к изменению всего порядка (полагаясь на стабильность порядка для сохранения «предыдущих» порядков), а не просто к проверке тай-брейков. Существует также проблема с удобочитаемостью, связанная с необходимостью указывать все в обратном порядке…5. @Jon: Поскольку я не хочу, чтобы порядок был таким же, как в исходном списке в этом случае, я хочу случайный порядок.
Ответ №2:
Чаще всего:
list.Sort((x,y) => {
int result = /* first comparison, for example
string.Compare(x.Name, y.Name) */
if (result == 0) result = /* second comparison,
for example x.Id.CompareTo(y.Id) */
...
if (result == 0) result = /* final comparison */
return resu<
});
или аналогичный (возможно, в классе сравнения, если это нетривиально).