Как я могу отсортировать List<object>, где объект всегда List<string>.
Я знаю, что уже есть вопросы, похожие на этот. Я пробовал Sort() или другие предложенные функции, но не смог решить.
Вот что у меня есть:
var result= new List<object>();
foreach (var data in myData)
{
result.Add(new List<string>() { data.Text, myData.Count().ToString() });
}
//This is how at the end the List<object> looks like:
[
[
"xxx",
"2"
],
[
"xxxx",
"2"
],
[
"xxx",
"2"
],
[
"xxxx",
"3"
],
[
"xxx",
"48"
],
[
"xxxx"
"18"
],
[
"xxxx",
"58"
],
[
"xxxx",
"1"
],
[
"xxxx",
"371"
],
[
"xxxx",
"3"
],
[
"xxxxx",
"2"
]
]
Мне нужно упорядочить List<object> вторым элементом списка, который всегда числовой
Кроме того, использование data в качестве имени коллекции и переменной итерации может привести к путанице, обязательно дайте эти четкие имена.





Вы можете отсортировать список следующим образом:
data = data.OrderBy(i => int.Parse(i[1])).ToList();
Но мы можем сделать еще лучше, если пропустим промежуточный шаг в вопросе и начнем с myData:
var data = myData.Select(md => (md.Text, md.Count())).OrderBy(md => md.Item2);
Это выглядит немного иначе, чем то, что вы просили, но объективно лучше по двум причинам:
Это позволяет нам сохранить значение int в более точном типе. Благодаря интернационализации/культурным проблемам преобразование между числами, датами и строками и обратно происходит гораздо медленнее и более подвержено ошибкам, чем нам хотелось бы верить. Поэтому этого следует избегать. Обычно гораздо лучше хранить специализированные числовые данные как можно дольше и преобразовывать их в строки только в последний момент. В этом случае мы дважды сохраняем операцию преобразования (по одному разу в каждом направлении), так что этот код уже должен быть НАМНОГО быстрее исходного.
Мы получаем IEnumerable<(string, int)> вместо List<List<string>>. IEnumerable<T> почти всегда намного эффективнее, чем List<T>, из-за меньшего использования памяти и возможности потоковой передачи данных по мере их поступления, вместо того, чтобы загружать всю настройку заранее.
Однако я понимаю, что вам могут понадобиться данные определенным образом, чтобы передать их на следующий шаг. Если вам действительно нужен джанки List<List<string>>, вы можете сделать это:
var data = myData.OrderBy(md => md.Count()).
Select(md => new List<string>{md.Name, md.Count().ToString()}).
ToList();
Обратите внимание, что из-за вышеперечисленных моментов, даже когда нам действительно нужен список, наиболее эффективным способом его получения было сохранение исходного int для заказа и создание IEnumerable перед созданием окончательного списка, и в зависимости от того, сколько времени потребуется для запуска. Count() может быть даже эффективнее использовать мое основное предложение IEnumerable<(string, int)> в качестве промежуточного шага:
var data = myData.Select(md => (md.Text, md.Count())).
OrderBy(md => md.Item2).
Select(md => new List<string> {md.Item1, md.Item2.ToString()}).
ToList();
Надеюсь, это также поможет вам по-другому взглянуть на этот вид обработки данных. Если IEnumerable с типизированными данными настолько эффективнее, что мы все равно используем его в качестве промежуточного шага, то, вероятно, это также лучший способ обработки передачи данных между методами или классами в первую очередь. Как минимум, вы можете хотя бы начать думать об удалении .ToList() с конца этих строк, так как вы всегда можете добавить его к переменной при передаче в функцию, которой все еще действительно нужен список.
Я бы порекомендовал вместо этого использовать строго типизированную структуру, очень быстрой здесь может быть кортеж, который теперь хорошо интегрирован в C# - может называть параметры. Или даже лучше создайте класс, применимый к вашему использованию.
Какую бы структуру вы ни использовали, если вы действительно хотите отсортировать свой список на месте с помощью пользовательской логики для сравнения объектов, вы можете предоставить свое выражение сортировки в качестве аргумента для метода Sort. Так, например, если все, что вам нужно, это второе число:
var result = new List<(string Text, int Count)>()
foreach (var data in myData)
{
result.Add(new ( data.Text, myData.Count()));
}
result.Sort((a,b) => a.Count - b.Count);
//here your list is already sorted
Хотя, честно говоря, обычно более желательным является не трогать исходный экземпляр и вместо этого создавать копию. Чего вы можете достичь с помощью OrderBy LINQ:
var sortedList = result.OrderBy((element) => element.Count)
Это чище, так как если вам не нужно работать с более сложными типами или выполнять вычисления, достаточно только указать, какое свойство вашего класса является ключом для использования для сортировки. Затем объекты будут возвращены в порядке, указанном значениями ключа, игнорируя остальную часть класса. Вы также можете передать более сложное выражение в качестве второго параметра, аналогично методу Sort.
Вы можете использовать удобные C# ValueTuples для простого создания строго типизированных данных.
var textCount = new List<(string Text, int Count)>();
foreach (var data in myData)
{
textCount.Add( (data.Text, data.Count()) );
}
// In-place sort of list
textCount.Sort((a, b) => a.Count.CompareTo(b.Count));
Если вы хотите отсортировать в порядке убывания, просто отмените сравнение
textCount.Sort((a, b) => -a.Count.CompareTo(b.Count));
или сравните b с a:
textCount.Sort((a, b) => b.Count.CompareTo(a.Count));
Или с LINQ
var textCount = myData
.Select(d => (d.Text, Count: d.Count()))
.OrderBy(t => t.Count) // Use OrderByDescending for reverse sort
.ToList();
// Test
foreach (var item in textCount) {
Console.WriteLine($"Text = {item.Text}, Count = {item.Count}");
}
Оба решения сохраняют счетчик как int. Это важно для сортировки. Числа, хранящиеся в виде строки, не будут сортироваться правильно. Вы получите алфавитный порядок, например «1», «11», «157», «2», «27», «3», «32».
Преимущество второго решения заключается в создании кортежей, сортировке, создании и заполнении списка в одном операторе.
Я не вижу смысла вам работать с объектом, в начале вы можете сделать такую фичу, как
var result = new List<(string, string)>();
result.OrderBy(x => x.Item2);
это облегчит вам заказ и добавление в список, подобный этому
foreach (var data in myData)
{
result.Add((data.Text, myData.Count().ToString()));
}
но обратите внимание, что лучшим способом для вас было бы отсортировать его по числовому индексу, поэтому вместо преобразования его в строку работайте с ним численно даже таким образом:
var result = new List<(string, int)>();
result.OrderBy(x => x.Item2);
foreach (var data in myData)
{
result.Add((data.Text, myData.Count().ToString()));
}
Я не рекомендую использовать только объект в виде списка из-за потери производительности, и если бы мне пришлось подумать об этом, мне пришлось бы создать абстрактный класс только для сравнения их списков и сортировки вручную.
К вашему сведению — вы почти никогда не захотите использовать
object. Создайте новый тип для хранения того, что вы хотите, или используйте существующий тип, который соответствует варианту использования. KeyValuePair может подойти, поскольку в вашем примере похоже, что у вас есть дубликаты «ключей».