How can I sort a List<object>
where the object is always List<string>
.
I know that there is already questions similar to this one.. I tried Sort() or other functions that was suggested but I couldn't solve.
This is what I have:
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"
]
]
I need to order the List<object>
by the second element of the list whis is always numer
CodePudding user response:
You can sort the list like this:
data = data.OrderBy(i => int.Parse(i[1])).ToList();
But we can do even better if we skip the intermediate step in the question, and start with myData
:
var data = myData.Select(md => (md.Text, md.Count())).OrderBy(md => md.Item2);
This is better for two reasons:
It lets us keep the
int
value in a more-precise type. Thanks to internationalization/cultural issues, converting back and forth between numbers or dates and strings is far slower and more error prone than we'd like to believe. It's therefore something to avoid. It's usually far better to keep the specialized numeric data for as long as possible, and only convert to strings at the last possible moment. In this case, we save the conversion operation twice (once in each direction), and so this code should already be MUCH faster than the original.We end up with an
IEnumerable<(string, int)>
, instead of aList<List<string>>
.IEnumerable<T>
is almost always greatly more efficient thanList<T>
, because of the reduced memory use and the ability to stream data as it comes, instead of needing to load the entire set up front.
If you really need the janky List<List<string>>
you can do this:
var data = myData.OrderBy(md => md.Count()).
Select(md => new List<string>{md.Name, md.Count().ToString()}).
ToList();
Note that because of the points above, even when we really want the list the most efficient way to get there was still to preserve the original int
for ordering and craft the IEnumerable
before creating the final list, and depending on how long it takes to run Count()
it might even be more efficient to use my main suggestion of IEnumerable<(string, int)>
as the intermediate step:
var data = myData.Select(md => (md.Text, md.Count())).
OrderBy(md => md.Item2).
Select(md => new List<string> {md.Item1, md.Item2.ToString()}).
ToList();
Hopefully this also helps you think differently about this kind of data processing. If an IEnumerable with typed data is so much more efficient that we use it as an intermediate step anyway, then its probably also the better way to handle passing the data between methods or classes in the first place. At minimum, you can at least start thinking about removing the .ToList()
from the end of these lines, since you can always append it to a variable when passing it to a function that still really needs a list.
CodePudding user response:
If you want to sort in place, try Sort
:
data.Sort((a, b) => Convert.ToInt32(a[1]).CompareTo(Convert.ToInt32(b[1])));
CodePudding user response:
I'd recommend to use strongly typed structure instead, a very quick one to use here can be a Tuple, which is now well integrated into C# - can name parameters. Or even better create a class applicable to your use.
Whatever structure you use, if you actually want to sort your list in place with a custom logic to compare the objects you can provide your sorting expression as an argument to Sort method. So for example if all you care for is the second number:
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
Although to be fair, normally more desired is to not touch the original instance and create a copy instead. Which you can achiever with LINQ's OrderBy:
var sortedList = result.OrderBy((element) => element.Count)
This is cleaner, as unless you need to work on more complex types or perform computation it is enough to only indicate which property of your class is the key to be use for sorting. The objects will be then returned in order indicated by the values of the key, ignoring the rest of the class. You can also pass more complex expression as a second parameter, similarly to the Sort method.
CodePudding user response:
I see no reason for you to work with the object in the beginning you can do a feature like
var result = new List<(string, string)>();
result.OrderBy(x => x.Item2);
this would make it easy for you to order and add to list like this
foreach (var data in myData)
{
result.Add((data.Text, myData.Count().ToString()));
}
but see that the best way would be for you to sort it through a numerical index so instead of transforming it into a string work with it numerically even in this way:
var result = new List<(string, int)>();
result.OrderBy(x => x.Item2);
foreach (var data in myData)
{
result.Add((data.Text, myData.Count().ToString()));
}
I don't recommend using only the object as a list due to performance loss and if I had to think about it I would have to create an abstract class just to compare their lists and sort manually
CodePudding user response:
You can use the handy C# ValueTuples to create strongly typed data in a simple way.
var textCount = new List<(string Text, int Count)>();
foreach (var data in myData)
{
textCount.Add( (data.Text, myData.Count()) );
}
// In-place sort of list
textCount.Sort((a, b) => a.Count.CompareTo(b.Count));
Or with LINQ
var textCount = myData
.Select(d => (d.Text, Count: d.Count()))
.OrderBy(t => t.Count)
.ToList();
// Test
foreach (var item in textCount) {
Console.WriteLine($"Text = {item.Text}, Count = {item.Count}");
}
Both solution store the count as int
. This is important for the sorting. Numbers stored as string will not sort correctly. You would get an alphabetical order like "1", "11", "157", "2", "27", "3", "32".
The second solution has the advantage to create a tuple, sort, create and fill the list in one statement.