Skullomania
Skullomania

Reputation: 2215

How do I sort a List<Type> by List<int>?

In my c# MVC project I have a list of items in that I want to sort in order of another list

var FruitTypes = new List<Fruit> {
    new Fruit { Id = 1, Name = "Banana"},
    new Fruit { Id = 2, Name = "Apple" },
    new Fruit { Id = 3, Name = "Orange" },
    new Fruit { Id = 4, Name = "Plum"},
    new Fruit { Id = 5, Name = "Pear" },
};

SortValues = new List<int> {5,4,3,1,2};

Currently my list is showing as default of fruit type.

How can I sort the Fruit list by SortValues?

Upvotes: 2

Views: 150

Answers (3)

Prasad Telkikar
Prasad Telkikar

Reputation: 16049

Time complexity:O(n) + TM of Linq.

  1. Declare list of fruits to store result.
  2. Iterate through each fruit type.
  3. Use Linq FirstOrDefault to get element by sorted value.

        List<int> SortValues = new List<int> { 5, 4, 3, 1, 2 };
        List<Fruit> result = new List<Fruit>();
        foreach (var element in SortValues)
        {
            Fruit f = FruitTypes.FirstOrDefault(fruitElement => fruitElement.Id == element);
            result.Add(f);
        }
    

Implementation: DotNetFiddler

Upvotes: 0

spender
spender

Reputation: 120440

It's unclear if you are sorting by the indexes in SortValues or whether SortValues contains corresponding Id values that should be joined.

In the first case:

First you have to Zip your two lists together, then you can sort the composite type that Zip generates, then select the FruitType back out.

IEnumerable<FruitType> sortedFruitTypes = FruitTypes
    .Zip(SortValues, (ft, idx) => new {ft, idx})
    .OrderBy(x => x.idx)
    .Select(x => x.ft);

However, this is simply sorting the first list by the ordering indicated in SortValues, not joining the ids.

In the second case, a simple join will suffice:

IEnumerable<FruitType> sortedFruitTypes =  SortValues
    .Join(FruitTypes, sv => sv, ft => ft.Id, (_, ft) => ft);

This works because Enumerable.Join maintains the order of the "left" hand side of the join.

Upvotes: 9

Eric Sondergard
Eric Sondergard

Reputation: 605

While there is almost certainly a more LINQ-y way, if you tend towards verbosity, you could accomplish this with an iterator function. For example:

public IEnumerable<Fruit> SortFruits(IEnumerable<Fruit> unordered, IEnumerable<int> sortValues)
{
    foreach (var value in sortValues)
        yield return unordered.Single(f => f.Id == value);
}

I like that it's explicit about what it's doing. You may consider throwing an exception when the number of items in each list is different, or maybe you just don't return an item if there is no sort value for it. You'll have to decide what the behaviour should be for "missing" values in either collection is. I think that having to handle these scenarios is a good reason to put it all in a single method this way, instead of a longer LINQ query.

Upvotes: 1

Related Questions