Freshblood
Freshblood

Reputation: 6441

How to sort collection quite specifically by linq

var ids = new int[] { 3, 2, 20, 1 };
var entities = categories.Where(entity => ids.Contains(entity.Id));

I have to sort entities by exactly same like in ids array. How can i do that ?

Upvotes: 0

Views: 264

Answers (3)

spender
spender

Reputation: 120518

This should do the trick (written off the top of my head, so may have mistakes)

var ids = new int[] { 3, 2, 20, 1 };
var ordering = ids.Select((id,index) => new {id,index});
var entities = 
    categories
        .Where(entity => ids.Contains(entity.Id))
        .AsEnumerable() //line not necessary if 'categories' is a local sequence
        .Join(ordering, ent => ent.Id, ord => ord.id, (ent,ord) => new {ent,ord})
        .OrderBy(x => x.ord.index)
        .Select(x => x.ent)

Upvotes: 6

Matthias Meid
Matthias Meid

Reputation: 12521

You may have a go with this:

public class Foo
{
    public void Bar()
    {
        int[] idOrder = new int[] { 3, 2, 20, 1 };
        var lookup = idOrder.ToDictionary(i => i,
            i => Array.IndexOf(idOrder, i));
        foreach(var a in idOrder.OrderBy(i => new ByArrayComparable<int>(lookup, i)))
            Console.WriteLine(a);
    }
}

public class ByArrayComparable<T> : IComparable<ByArrayComparable<T>> where T : IComparable<T>
{
    public readonly IDictionary<T, int> order;
    public readonly T element;

    public ByArrayComparable(IDictionary<T, int> order, T element)
    {
        this.order = order;
        this.element = element;
    }

    public int CompareTo(ByArrayComparable<T> other)
    {
        return this.order[this.element].CompareTo(this.order[other.element]);
    }
}

This works for unique elements only, but the lookup efford is constant.

Upvotes: 0

Merlyn Morgan-Graham
Merlyn Morgan-Graham

Reputation: 59151

You could use OrderBy with the index of the Ids in ids.

To get the index of an Id from ids, you could create a map of Id to index. That way you can look up the index in almost constant time, instead of having to call IndexOf and traverse the whole list each time.

Something like this:

var idToIndexMap = ids
    .Select((i, v) => new { Index = i, Value = v })
    .ToDictionary(
        pair => pair.i,
        pair => pair.v
        );

var sortedEntities = categories
    .Where(e => ids.Contains(e.Id))
    .ToList() // Isn't necessary if this is Linq-to-Objects instead of entities...
    .OrderBy(e => idToIndexMap[e.Id])
    ;

Upvotes: 0

Related Questions