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
4
Answers
You can sort the list like this:
But we can do even better if we skip the intermediate step in the question, and start with
myData
:This looks a little different than what you asked for, but it’s objectively 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.However, I understand you may need the data a specific way in order to pass it on to the next step. If you really do need the janky
List<List<string>>
you can do this: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 theIEnumerable
before creating the final list, and depending on how long it takes to runCount()
it might even be more efficient to use my main suggestion ofIEnumerable<(string, int)>
as the intermediate step: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.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:
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:
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.
You can use the handy C# ValueTuples to create strongly typed data in a simple way.
If you wanted to sort in descending order just negate the comparison
or compare b to a:
Or with LINQ
Both solutions store the count as an
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 tuples, sort, create and fill the list in one statement.
I see no reason for you to work with the object in the beginning you can do a feature like
this would make it easy for you to order and add to list like this
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:
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