Custom sorting with LINQ

前端 未结 6 1123
被撕碎了的回忆
被撕碎了的回忆 2021-01-15 04:16

It seems that i\'m missing something trivial.

Anyway, here it goes:

var order = new[]{1,3,2};
var foos = new[]{new Foo{Id=1}, new Foo{Id=2}, new Fo         


        
相关标签:
6条回答
  • from o in order.Select((o, i) => new { o, i })
    join f in foos on o.o equals f.Id
    orderby o.i
    select f;
    
    0 讨论(0)
  • 2021-01-15 04:25

    I'd probably use a Dictionary<int,int> of id/ordering pairs to make the order lookup O(1) if I had a lot of these to do. Note that you would also need to handle cases where your values are missing from the ordering -- I chose to move them to the end.

    var order = new Dictionary<int,int>();
    order.Add( 1, 1 );
    order.Add( 2, 3 );
    order.Add( 3, 2 );
    
    var orderedFoos = foos.OrderBy( f => order.Contains(f.Id) ? order[f.Id] : int.MaxValue );
    
    0 讨论(0)
  • 2021-01-15 04:26

    Okay, the question doesn't seem to be entirely clear to me, so I'll try to clarify what I think you're asking:

    • You have a sequence of IDs, in the desired order
    • You have a collection of objects, not in the right order, but with corresponding IDs
    • You want to get the collection of objects in the same order as the ID sequence

    Correct?

    That would be:

    var orderedFoos = from orderedId in order
                      join foo in foos on orderedId equals foo.Id into groups
                      select groups.Single();
    

    You need a join ... into to verify that you don't have any missing or duplicate IDs in foos. It won't, however, detect if you've got missing or duplicate IDs in order. If you know that everything will be correct (i.e. there will be exactly one entry in foos for every entry in order and vice versa) then a simple join is okay:

    var orderedFoos = from orderedId in order
                      join foo in foos on orderedId equals foo.Id
                      select foo;
    

    which can be expressed in dot notation as:

    var orderedFoos = order.Join(foos, order => order, foo => foo.ID, (o, f) => f);
    
    0 讨论(0)
  • 2021-01-15 04:26

    You can do this using a nested query, but it is quite inefficient with O(n²).

    var result = order.Select(o => foos.Single(f => f.Id == o));
    

    If 'order' may contain ids not present in 'foos', you should use SingleOrDefault(). If foos might contain duplicate ids, you should use First() or FirstOrDefault().

    var result = order
        .Select(o => foos.FirstOrDefault(f => f.Id == o))
        .Select(f => f != null);
    

    Maybe even a join will work, but I am not sure if it preserves the order.

    var result = Enumerable.Join(order, foos, o => o, f => f.Id, (o, f) => f);
    

    As Jon mentioned, the join will only work correctly if the input is well formed in the same way as required by my first suggestion.

    0 讨论(0)
  • 2021-01-15 04:36

    Is this what you are trying to do?

    foos.OrderBy(f => order[f.Id-1]);
    

    If you foreach now on the output of that, printing the ID, you get: 1,3,2

    0 讨论(0)
  • 2021-01-15 04:43
    var order = new[] { 1, 3, 2 };
    var foos = new[] { new Foo { Id = 1 }, new Foo { Id = 2 }, new Foo { Id = 3 } };
    
    var query = from o in order
                join foo in foos on o equals foo.Id
                select foo;
    
    var foos2 = query.ToArray();
    
    0 讨论(0)
提交回复
热议问题