2

I have a IEnumerable<Process>.

public class Process
{
    public string Id { get; set; }
    public string Name { get; set; }
    public int Type { get; set; }
}

I want to order this IEnumerable<Process> based on Type. The ordering should be based on a predefined sequence.

The sequence is: 7,3,4,1. So, if the collection has any of these values they should appear in the order first 7, then 3 etc.

Any other Type then on should be in ascending order.

What is the correct way to order this based on a predefined sequence? Should I define the sequence itself as a enum?

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
Sartorial
  • 173
  • 1
  • 8

4 Answers4

4

If the sequence of numbers is a List<int> you can order by the index:

var ordered = processes
    .OrderByDescending(p => numbers.Contains(p.Type))
    .ThenBy(p => numbers.IndexOf(p.Type));

or, a little bit more efficient and readable with LINQ's query syntax:

var ordered = from proc in processes
              let index = numbers.IndexOf(proc.Type)
              orderby index == -1, index
              select proc;

index == -1 returns a bool where true is "higher" than false. That's the whole trick.

By the way, Array has also an IndexOf method:

let index = Array.IndexOf(numArray, proc.Type)
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
1

Create a lookup from those items to the value to sort on (in this case, the index in that list) and then you can easily project each of the values to the value to sort on:

var typeOrders = new int[]{7,3,4,1};
var typeOrderLookup = typeOrders.Select((type,i)=>new{type,i})
    .ToDictionary(pair => pair.type, pair => pair.i);

var query = someData.OrderBy(process => typeOrderLookup[process.Type]);
Servy
  • 202,030
  • 26
  • 332
  • 449
0

What I've got from you question is, if your process list contains any Process with Type in sequence = (7, 3, 4, 1), then those processes should appear first with the ordering based on that sequence, then the rest of the process list should be ordered based on Type in ascending order. What I've done is basically create two lists and finally union them. The first list contains only processes with Types in sequence which is sorted based on the sequence and the second list contains the rest of the original list simply ordered by Type.

Here is your answer with a sample data:

List<Process> processList = new List<Process>() 
{
    new Process() { Id = "1", name = "1", Type = 7},
    new Process() { Id = "2", name = "2", Type = 1},
    new Process() { Id = "3", name = "3", Type = 4},
    new Process() { Id = "4", name = "4", Type = 3},
    new Process() { Id = "5", name = "5", Type = 9},
    new Process() { Id = "6", name = "6", Type = 8},
};

List<int> sequence = new List<int>() { 7, 3, 4, 1 };
var pl = processList.Where(p => sequence.Contains(p.Type))
                                        .OrderBy(p => sequence.IndexOf(p.Type))
                    .Union(processList.Where(p => !sequence.Contains(p.Type))
                                      .OrderBy(p => p.Type));
Arin Ghazarian
  • 5,105
  • 3
  • 23
  • 21
0

We can do it by one expression:

var order = new int[] { 7, 3, 4, 1 };
var orderedProccesses = order.Select(i => processes.FirstOrDefault(p => p.Type == i))
    .Where(p => p != null)
    .Concat(processes.Where(p => !order.Contains(p.Type)).OrderBy(p => p.Type))
    .ToArray();

First map the defined list of order to the corresponding Processes:

order.Select((i) => processes.FirstOrDefault(p => p.Type == i))

Then, a sanity check, because it's not guaranteed that every item in the order to have a corresponding Process:

    .Where((p) => p != null)

Then add the rest of the Processes and sort them by Type

    .Concat(processes.Where((p) => !order.Contains(p.Type)).OrderBy((p) => p.Type))
homam
  • 1,945
  • 1
  • 19
  • 26