38

I have two generic list objects, in which one contains ids and ordering, and the other a bunch of ids with each id in the second list having an id reference to the first list, for example;

public class OptionType { public int ID { get; set; } public int Ordering { get; set; } } public class Option { public int ID { get; set; } public int Type_ID { get; set; } } 

Obviously I can do a simple sort on a list of OptionTypes by doing

types_list.OrderBy(x => x.Ordering); 

Question is though, how could I go about ordering an 'options_list' by utilising the 'Type_ID' on the object which would relate to the ordering of the types_list. As in something like (obviously this isn't valid - but hopefully you will get the idea!)

options_list.OrderBy(x => x.Type_ID == types_list.OrderBy(e => e.Ordering)); 
1
  • 4
    I don't understand, can you give us a sample of what the sorted output might look like given some input? Commented Aug 12, 2010 at 17:05

4 Answers 4

41

You should be able to use a join to produce your desired output. Example using query syntax.

var orderedOptions = from option in options_list join type in types_list on option.Type_ID equals type.ID orderby type.Ordering select option; 
Sign up to request clarification or add additional context in comments.

2 Comments

When I use this method, I got The incoming request has too many parameters error, what should I do?
Nailed it. Cheers. I was looking for something similar. Quite often forget about this type of linq syntax :-/
38

List.FindIndex() is your friend when listA is small and already sorted:

var orderedB = listB.OrderBy(b => listA.FindIndex(a => a.id == b.id)); 

Working example: https://dotnetfiddle.net/CpLeFU

As @goodeye pointed out in the comments, performance will be a nightmare on larger lists. Use the accepted answer in that case.

2 Comments

This is much nicely simpler than the joins. This assumes listA is already sorted in the desired order (type.Ordering). That is, it's using listA's position in the list, not a property of listA. It's also doing multple FindIndex, so may have a performance issue on large lists.
Man you really saved my day
10

I like Lambda syntax, so I came up with this equivalent. I can see how query syntax is cleaner for joins.

var orderedOptions = options_list .Join( types_list, option => option.Type_ID, type => type.ID, (option, type) => new { Option = option, Type = type }) .OrderBy(x => x.Type.Ordering) .Select(x => x.Option); 



For a slight reduction (of what, I'm not sure), this creates the new object with just the Ordering property, instead of the entire Type class. Not much different here, but I had a large class with the sort data, and only need the sorting property. Don't know if it mattered, but it was clearer to read.

var orderedOptions = options_list .Join( types_list, option => option.Type_ID, type => type.ID, (option, type) => new { Option = option, Ordering = type.Ordering }) .OrderBy(x => x.Ordering) .Select(x => x.Option); 

It looks like the query syntax lets you order within the initial query, while lambda requires ordering after the join creates the new object. Maybe they're really doing the same thing under the covers though: creating the joined object, to be sorted then selected.

Comments

1

You can always use a more structured way by implementing the IComparer interface with:

public class OptionComparer : IComparer<Option> { // <Id, Sort> private readonly Dictionary<int, int> _orderedOptionTypeIds; public OptionComparer(IEnumerable<OptionType> optionTypes) { _orderedOptionTypeIds = optionTypes .OrderBy(x => x.Ordering) //If its not sorted already .ToDictionary(x => x.ID, x => x.Ordering); } public int Compare(Option x, Option y) { int xIndex, yIndex; if (!_orderedOptionTypeIds.TryGetValue(x.Type_ID, out xIndex)) { xIndex = int.MaxValue; } if (!_orderedOptionTypeIds.TryGetValue(y.Type_ID, out yIndex)) { yIndex = int.MaxValue; } return xIndex.CompareTo(yIndex); } } 

Then the usage may be:

IEnumerable<OptionType> optionTypes = Get(...); options_list.OrderBy(x => x, new OptionComparer(optionTypes)); 

2 Comments

Nice idea, but very resource-intensive because _orderedOptionTypeIds will be enumerated twice for each comparison. That could be alleviated somewhat by using a dictionary (id => index), but still, the other solutions are better, resourcewise.
You are right, I assumed the _orderedOptionTypeIds is small enough, (I used it for a fixed small list ~ 10 items), but I've updated the answer for a wider purpose

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.