Totoro
Totoro

Reputation: 101

Best way to sort a data structure of assorted objects by one sort variable?

Given a list/array of objects containing some other objects, I'm looking to sort by using one of the parameters. For example, if I have a multi-dimensional array of ints and I want to sort by their midpoint.

The way I'm doing it is to create an anonymous object containing 1. the midpoint and 2. a list of the 2 ints for each entry. Then enlist these objects and use reflection to access and sort by the midpoint parameter.

Is there a more standard approach to this problem? It's a bit clumsy because I have to create a dummy and clear the anonymous list object in order to do it.

class Program
{
    static void Main(string[] args)
    {
        int[,] input = new int[,] { { 5, 6 }, { 0, 1 }, { 2, 20 } };

        var itemList = new []{ 
          new { sortVar = 0.1f, item = new List<int>() } }.ToList();

        itemList.Clear();

        for(int i=0; i<input.GetLength(0); i++)
        {
            List <int> temp = new List<int> (new int[] { input[i,0], input[i, 1] });

            var item = new { 
              sortVar = float.Parse((input[i,0]+input[i,1]).ToString())/2,
              item = temp };

            itemList.Add(item);
        }

        var itemList2 = itemList.OrderBy(x => x.sortVar);
    }
}

Upvotes: 1

Views: 115

Answers (2)

Mong Zhu
Mong Zhu

Reputation: 23732

A short linq version of your approach would be to use the Average method directly in the OrderBy clause (if you are inclined to use a 2-D List):

List<List<int>> input_List = new List<List<int>> 
{
    new List<int>{ 5, 6 }, 
    new List<int>{ 0, 1 }, 
    new List<int>{ 2, 20 } 
};

var output = input_List.OrderBy(x=>x.Average()).ToList();   

EDIT:

If you need to order it by the median you can use this expression:

var output = input_List.OrderBy(x=>x.OrderBy(y=>y).ElementAt(x.Count / 2)).ToList();    

Upvotes: 1

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186668

I suggest changing 2D array into jagged one (array of array):

   int[][] input = new int[][] { 
     new int[]{ 5,  6 }, 
     new int[]{ 0,  1 }, 
     new int[]{ 2, 20 } }; 

And extract the criterium (MidPoint) as a method

   private static double MidPoint(IEnumerable<int> value) {
      int[] sorted = value.OrderBy(item => item).ToArray(); 

      // / 2.0: we don't want integer division: (1 + 4) / 2 == 2 when (1 + 4) / 2.0 == 2.5
      return sorted.Length % 2 == 0
        ? (sorted[sorted.Length / 2 - 1] + sorted[sorted.Length / 2]) / 2.0
        : sorted[sorted.Length / 2];
    } 

Tnen if you want to sort in place use Array class:

 // input will be sorted 
 Array.Sort(input, (a, b) => MidPoint(a).CompareTo(MidPoint(b)));

If you want to create a new sorted array:

 // we'll have to arrays: initial (unsorted) input, and sorted sortedInput
 var sortedInput = input
   .OrderBy(line => MidPoint(line))
   .ToArray();

Upvotes: 1

Related Questions