Как я могу выполнить вид на двух различных критериях?
Например, у меня есть объекты человека как:
Person
со свойствами FirstName
(строка), LastName
, и Rank
(интервал).
Данные в качестве примера как так:
Xavier Smith 1
Alexander Smith 2
Alexander Smith 1
Bob Hawke 2
Это должно отсортировать на FirstName в алфавитном порядке, затем на разряде, например, получающийся:
Alexander Smith 1
Alexander Smith 2
Bob Hawke 2
Xavier Smith 1
До сих пор я попробовал следующее, но это не работает правильно:
peopleList
List<Person>
peopleList.Sort(new Comparison<Person>((x,y) => x.Rank.CompareTo(y.Rank)));
peopleList.Sort(new Comparison<Person>((x, y) => string.Compare(x.Name, y.Name)));
Спасибо
править: чтобы не изменять мой код слишком много, я действительно хочу сохранить список, если я изменяю вышеупомянутые строки на:
peopleList.OrderBy(person => person.FirstName).ThenBy(person => person.Rank).ToList();
Дал бы тот же самый список, просто отсортированный правильно, корректный?
Подход LINQ
С LINQ вы можете использовать OrderBy и ThenBy :
var result = peopleList.OrderBy(p => p.FirstName).ThenBy(p => p.Rank);
Это вернет IEnumerable
. Если вам действительно нужен List
, добавьте в конец .ToList ()
.
Если вы хотите использовать метод Sort
, вам необходимо написать собственный компаратор.
РЕДАКТИРОВАТЬ: использование ToList ()
возвращает новый список. Если вы хотите отсортировать существующий список, вам следует использовать метод Sort
, который не возвращает список, а работает с текущим списком (это метод void
).
Подход Sort / Comparer
Используйте: list.Sort (new PersonComparer ());
Вот код компаратора. Он был адаптирован из примера MSDN , поэтому я рекомендую прочитать комментарии, которые они использовали, чтобы понять, почему он так структурирован.
public class PersonComparer : IComparer<Person>
{
public int Compare(Person x, Person y)
{
if (x == null)
{
if (y == null)
{
return 0;
}
else
{
return -1;
}
}
else
{
if (y == null)
{
return 1;
}
else
{
int retval = x.FirstName.CompareTo(y.FirstName);
if (retval != 0)
{
return retval;
}
else
{
return x.Rank.CompareTo(y.Rank);
}
}
}
}
}
Вы действительно были очень близки с синтаксисом лямбда сортировки на месте. Вы просто упустили тот факт, что лямбды могут быть заключены в собственную область видимости:
peopleList.Sort(new Comparison<Person>((x,y) =>
{
int result = x.FirstName.CompareTo(y.FirstName);
return (result != 0) ? result : x.Rank.CompareTo(y.Rank);
}));
Это немного меньше усилий, чем написать свой собственный IComparer
!
Мне нравится ответ LINQ. Если это не вариант, вы всегда можете использовать
(x,y) => 2*string.Compare(x.Name,y.Name) + x.Rank.CompareTo(y.Rank)
чтобы строковое сравнение всегда доминировало, если оно не равно 0
Другие ответы кажутся более элегантными, чем этот, и заставляют меня чувствовать себя еще большим профаном, однако если вы поймете, как сортировать таким образом, вы сможете сортировать любые списки любым способом, не зная почти ничего. И нет необходимости писать целый новый класс (хотя написание класса comparer может быть полезным, если вы сортируете другие похожие списки в других частях вашего кода).
peopleList.Sort((x, y) =>
{
int compare = x.FirstName.CompareTo(y.FirstName);
if (compare != 0)
return compare;
compare = x.Rank.CompareTo(y.Rank);
if (compare != 0)
return compare;
return x.LastName.CompareTo(y.LastName);
});