6

I have a List of key value pairs which I would like to sort according to an order specified in another array.

var requiredOrder = new String[] { "PH", "HH", "PR", "SR", "UN", "UD", "WD", "WE", "OT" };


        var listToBeSorted = new List<KeyValuePair<string, string>>() {
            new KeyValuePair<string, string>("A","PR"),
            new KeyValuePair<string, string>("B","PH"),
            new KeyValuePair<string, string>("C","HH"),
            new KeyValuePair<string, string>("D","WD"),
            new KeyValuePair<string, string>("E","OT"),
            new KeyValuePair<string, string>("F","UN"),
            .....
        };

'listToBeSorted' is a really a collection I fetch for a dropdownlist that needs to be sorted by value according to the 'requiredOrder' array.

9
  • Can the list to be sorted have duplicates? For example 2 pairs with PR as second element Commented Jun 19, 2018 at 9:51
  • 1
    No. The values are unique. Sorry I've ammended my code snippet to avoid confusion Commented Jun 19, 2018 at 9:52
  • Ahh great. Then the sort is not needed since it would be N^2LogN (index of, times sort). Better loop requiredOrder and find within list (if exists) and push to result. N^2... (I can't write an answer right now) Commented Jun 19, 2018 at 9:54
  • If you can convert listToBeSorted to an array you can use Array.Sort(Array keys, Array items) Commented Jun 19, 2018 at 9:57
  • 1
    you can loop over the first array and get the code (such as PH) and change the index of it in the second array to match the index in the first one, got me? Commented Jun 19, 2018 at 9:58

5 Answers 5

8

You can sort by the index of the item in the requiredOrder array by using Array.IndexOf()

listToBeSorted = listToBeSorted.OrderBy(x => Array.IndexOf(requiredOrder, x.Value)).ToList();
Sign up to request clarification or add additional context in comments.

1 Comment

Just what I needed. Nice and simple too, thanks :)
2

I loved Lasse's answer, but it can be even a bit neater. Try this:

var requiredOrder = new String[] { "PH", "HH", "PR", "SR", "UN", "UD", "WD", "WE", "OT" };

var listToBeSorted = new List<KeyValuePair<string, string>>()
{
    new KeyValuePair<string, string>("A","PR"),
    new KeyValuePair<string, string>("B","PH"),
    new KeyValuePair<string, string>("C","HH"),
    new KeyValuePair<string, string>("D","WD"),
    new KeyValuePair<string, string>("E","OT"),
    new KeyValuePair<string, string>("F","UN"),
};

var lookup = listToBeSorted.ToLookup(x => x.Value);

var sorted =
    from x in requiredOrder
    from y in lookup[x]
    select y;

That gives:

sorted

Comments

2

Considered that the list to be sorted has only uniques:

The O(N^2) approach will perform better than OrderBy with IndexOf, the latter being NLogN * N = (N^2)LogN (since the indexof, which hides N, performed at each step of the sort), see comment within the code

var sortedList = new List<KeyValuePair<string, string>>();

for (int i = 0; i < requiredOrder.Length; i++) { // "i" spares indexof.
    for (int j = 0, l = listToBeSorted.Count; j < l; j++) {
        if (requiredOrder[i].Equals(listToBeSorted[j].Value)) {
            sortedList.Add(listToBeSorted[j]);
            //might as well Remove from listToBeSorted to decrease complexity.
            //listToBeSorted.RemoveAt(j);
            break;
        }
    }
}

Not the classiest of code, but can be further improved.

If you Remove from listToBeSorted (and adjust loop indexes) complexity will tend to NLogN (to be accurate, still 1/2 N^2) and space complexity to "0 additional".

2 Comments

It's trivial, but it shows that the code wasn't tested. Three errors. I'll post the corrected code.
Ok thanks for the edit. I was asked to post this, I have no C# IDE at hand
1

I don't see the need for any sorting at all.

var requiredOrder = new String[] { "PH", "HH", "PR", "SR", "UN", "UD", "WD", "WE", "OT" };

var listToBeSorted = new List<KeyValuePair<string, string>>() {
    new KeyValuePair<string, string>("A","PR"),
    new KeyValuePair<string, string>("B","PH"),
    new KeyValuePair<string, string>("C","HH"),
    new KeyValuePair<string, string>("D","WD"),
    new KeyValuePair<string, string>("E","OT"),
    new KeyValuePair<string, string>("F","UN"),
    .....
};

Simply transform the listToBeSorted into a lookup table, then grab all the objects from this table when iterating the requiredOrder:

var lookup = listToBeSorted.ToLookup(kvp => kvp.Value);
var result =
    from key in requiredOrder
    from obj in lookup[key]
    select obj;

3 Comments

Actually, I just checked this answer and it falls over if any of the values is a duplicate in listToBeSorted.
@Enigmativity Fixed, it will now produce them all. Not entirely sure it's a stable sort though.
I believe it should be stable. .ToLookup preserves the order of the elements under each key.
0

Here's another approach:

  1. Create an array of indices the same length as the array to be sorted.
  2. Use Array.Sort(Array keys, Array items) to sort the indices into the correct order.
  3. Use the index array to access the list in the desired order.

Here's the sample code - the lines corresponding to the steps in the algorithm above are annotated with (1), (2) and (3):

using System;
using System.Collections.Generic;
using System.Linq;

namespace Demo
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            var requiredOrder = new [] { "PH", "HH", "PR", "SR", "UN", "UD", "WD", "WE", "OT" };

            var listToBeSorted = new List<KeyValuePair<string, string>>() {
                new KeyValuePair<string, string>("A", "PR"),
                new KeyValuePair<string, string>("B", "PH"),
                new KeyValuePair<string, string>("C", "HH"),
                new KeyValuePair<string, string>("D", "WD"),
                new KeyValuePair<string, string>("E", "OT"),
                new KeyValuePair<string, string>("F", "UN"),
                new KeyValuePair<string, string>("G", "UD"),
                new KeyValuePair<string, string>("H", "WE"),
                new KeyValuePair<string, string>("I", "SR")
            };

            int[] indices = Enumerable.Range(0, requiredOrder.Length).ToArray(); // (1)

            Array.Sort(requiredOrder, indices); // (2)

            foreach (var index in indices) // (3)
            {
                Console.WriteLine(listToBeSorted[index].Key);
            }
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.