Click here to Skip to main content
15,886,919 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi!

I have the folowing list:

C#
var arrayList = new List<List<int>>();
List<int> 
      row = new List<int>() {5, 9, 17, 40, 99};
      arrayList.Add(row.ToList());
      row = new List<int>() {12, 24, 30, 45, 80};
      arrayList.Add(row.ToList());
      row = new List<int>() {5, 9, 17, 16, 99};
      arrayList.Add(row.ToList());
      row = new List<int>() {1, 5, 8, 15, 25};
      arrayList.Add(row.ToList());


I need to sort this list so that I get the following result:
1 5 8 15 25
5 9 17 16 99
5 9 17 40 99
12 24 30 45 80


So, actually, I need to order the list first by column and then by row values. Something similar to Custom Sort in Excel.

How may I accomplish this task?

Thank you in advance!

What I have tried:

I have figured out how to sort ascending the values in rows of the list. However, I can not obtain the result for sorting a columns and rows.
Posted
Updated 22-Jul-19 9:59am
Comments
Patrice T 13-Jul-19 15:49pm    
show your work and explain where you are stuck.
BillWoodruff 14-Jul-19 1:21am    
"I have figured out how to sort ascending the values in rows of the list."

show your code for that, and we may have a clue about how to help you.

this row : 5 9 17 16 99

is not sorted ... assuming you want it in ascending order

C#
using System.Linq; // required:

List<List<int>> list = new List<List<int>>
{
   new List<int> {5, 9, 17, 40, 99},
   new List<int> {12, 24, 30, 45, 80},
   new List<int>() {5, 9, 17, 16, 99},
   new List<int>() {0, 9, 17, 16, 0},
   new List<int>() {129, 5, 8, 15, 25}
};
Start by ordering each row:
C#
IEnumerable<List<int>> rlist = list.Select(
    lst => lst.OrderBy(i => i).ToList()
);
Then, order by the values at index #0 in each row:
C#
List<List<int>> sortedList = = rlist.OrderBy(lst => lst[0]).ToList();
You can chain these together:
var rlist = list
    .Select(lst => lst.OrderBy(i => i).ToList())
        .OrderBy(lst => lst[0])
    .ToList();
 
Share this answer
 
Comments
RickZeeland 14-Jul-19 3:59am    
5d :)
Maciej Los 14-Jul-19 7:06am    
Excellent!
Zaur Bahramov 14-Jul-19 8:19am    
It works, but sorting only by lst[0], the first field of the row, however it does not work on sorting by second, third, fourth and fifth field.
Result of this code is:
0 | 0 | 9 | 16 | 17 | =
5 | 9 | 17 | 40 | 99 |
5 | 9 | 16 | 17 | 99 |
5 | 8 | 15 | 25 | 129 |
12 | 24 | 30 | 45 | 80 |

But it hsould be like this:
0 | 0 | 9 | 16 | 17 | =
5 | 8 | 15 | 25 | 129 |
5 | 9 | 16 | 17 | 99 |
5 | 9 | 17 | 40 | 99 |
12 | 24 | 30 | 45 | 80 |
Zaur Bahramov 14-Jul-19 8:38am    
I've tried to do something like the following, but it doesn't work, since in this case it sorts by the last field...
for (int i = 0; i<5; i++) {
sortedList = rlist.OrderBy(lst => lst[i]).ToList();
}
Zaur Bahramov 14-Jul-19 8:44am    
sortedList = rlist.OrderBy(lst => lst[0]).ThenBy(lst => lst[1]).ThenBy(lst => lst[2]).ThenBy(lst => lst[3]).ThenBy(lst => lst[4]).ToList();

I've used a thenby and result is OK. However, if the number of fields in the row is not 5, like in the above example, but arbitrary, how to loop through ThenBy to continue sorting according to each column starting from the first one?
Take a look at IComparable, here is an example:
https://www.dotnetperls.com/icomparable[^]
 
Share this answer
 
Comments
BillWoodruff 14-Jul-19 1:27am    
this is really a vague suggestion, given the complexity of the OP's goal of a multiple sort.
RickZeeland 14-Jul-19 2:54am    
Just being more strict, it's not good to write all the code for them, that way they will not learn :)
When you look closer at the example you will see that it does exactly what is wanted here, it is a flexible way to sort on multiple values.
BillWoodruff 14-Jul-19 6:09am    
A fair riposte ! I did read the example you posted which illustrates an interesting technique where, within one sort, you switch sort criterion in the CompareTo method. However, I can't see the relevance of that to this question which, imho, requires two separate sorts.

fyi: it would be "nice" if the Linq could be simpler, not requiring multiple calls to 'ToList()
Hi,
This is just an idea, but you could concatenate the values in each row into comparable strings and just sort that list of strings.
You could then convert result back into integers if you like.


var arrayList = new List<List<int>>();
List<int> row = new List<int>() { 5, 9, 17, 40, 99 };
arrayList.Add(row.ToList());
row = new List<int>() { 12, 24, 30, 45, 80 };
arrayList.Add(row.ToList());
row = new List<int>() { 5, 9, 17, 16, 99 };
arrayList.Add(row.ToList());
row = new List<int>() { 1, 5, 8, 15, 25 };
arrayList.Add(row.ToList());

// maximum number of digits in input numbers
int maxDigits = 2;

// build list of strings, each a concatenation of numbers in row, left padded with "0"s
List<string> stringList = new List<string>();
foreach (List<int> dataRow in arrayList)
{
    string rowStr = "";
    foreach (int value in dataRow) rowStr += value.ToString().PadLeft(maxDigits, '0');
    stringList.Add(rowStr);
}

Console.WriteLine("input converted to list of strings:");
foreach (var s in stringList) Console.WriteLine(s);
Console.WriteLine();

// sort list of strings
stringList.Sort();

Console.WriteLine("sorted list of strings:");
foreach (var s in stringList) Console.WriteLine(s);


Output:

input converted to list of strings:
0509174099
1224304580
0509171699
0105081525

sorted list of strings:
0105081525
0509171699
0509174099
1224304580
Press any key to continue . . .
 
Share this answer
 
If I've understood what you're trying to do, you need to implement a custom IComparer<T>[^] to sort the list:
C#
public sealed class ListComparer<T> : IComparer<IReadOnlyList<T>> where T : IComparable
{
    public int Compare(IReadOnlyList<T> left, IReadOnlyList<T> right)
    {
        if (left is null) return right is null ? 0 : -1;
        if (right is null) return 1;
        
        var innerComparer = Comparer<T>.Default;
        int count = Math.Min(left.Count, right.Count);
        for (int index = 0; index < count; index++)
        {
            int result = innerComparer.Compare(left[index], right[index]);
            if (result != 0) return result;
        }
        
        return left.Count.CompareTo(right.Count);
    }
}
With that in place, it's then easy to sort your list:
C#
var list = new List<List<int>>
{
    new List<int> { 5, 9, 17, 40, 99 },
    new List<int> { 12, 24, 30, 45, 80 },
    new List<int> { 5, 9, 17, 16, 99 },
    new List<int> { 1, 5, 8, 15, 25 }
};

// Option 1: sort in-place:
list.Sort(new ListComparer<int>());

// Option 2: use LINQ:
var sortedList = list.OrderBy(l => l, new ListComparer<int>()).ToList();
The output in either case matches the desired output from your question.

This comparer will cope with lists of different lengths; if one list is equal to the other list with some extra elements appended, the longer list will be sorted after the shorter one.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900