Search code examples
c#linqsql-order-byigrouping

How to order IGrouping without changing its type?


I have an oject of the type IGrouping and would like to order the elements inside the group without changing the type of the object.
In other words, I have

var tmp = group.OrderBy(x => x);

with group being of type IGrouping<int, someanonymousclass> and I want tmp to be of type IGrouping<int, someanonymousclass> as well.

One workaround would be to change it to a Dictionary or a new anonymous class, but I want tmp to specificly be of type IGrouping<int, someanonymousclass>.

In other words: I want to sort the elemnts of my IGrouping<int, someanonymousclass> object. If I use OrderBy it changes the type of group to IOrderedEnumerable and I cannot access group.Key anymore. How can i sustain the type of group?

Example:

var states = SimulationPanel.innerPanel.Children.OfType<StateBar>().Where(x => x.isSensorState())
                .GroupBy(x => (SensorElement)x.BauplanElement,
                         x => new
                         {
                             Start = (decimal)(x.Margin.Left / SimulationPanel.Zoom),
                             Width = (decimal)(x.Width / SimulationPanel.Zoom),
                             State = x.State
                         });

var group = states.First();
var tmp = group.OrderBy(x => x.Start);
var key = tmp.Key;    //This does not work, because tmp is not of type IGrouping

I know I could use OrderBy before grouping. I don't want to do that, though.


Solution

  • If you can, just put the ordering earlier, e.g.

    var states = SimulationPanel.innerPanel
           .Children
           .OfType<StateBar>()
           .Where(x => x.isSensorState())
           .OrderBy(x => (decimal)(x.Margin.Left / SimulationPanel.Zoom))
           .GroupBy(...);
    

    (Or put a Select before the OrderBy and make the GroupBy simpler.)

    If you really need this though, you could write your own IGrouping<TKey, TElement> implementation and an extension method to order the elements and retain them in a list:

    public static class GroupExtensions
    {
        public static IGrouping<TKey, TElement> OrderGroupingBy<TKey, TElement, TOrderKey>
            (this IGrouping<TKey, TElement> grouping,
             Func<TElement, TOrderKey> orderKeySelector)
        {
            return new GroupingImpl<TKey, TElement>
                (grouping.Key, grouping.OrderBy(orderKeySelector));
        }
    
        private class GroupingImpl<TKey, TElement> : IGrouping<TKey, TElement>
        {
            private readonly TKey key;
            private readonly List<TElement> elements;
    
            internal GroupingImpl(TKey key, IEnumerable<TElement> elements)
            {
                this.key = key;
                this.elements = elements.ToList();
            }
    
            public TKey Key { get { return key; } }
    
            public IEnumerator<TElement> GetEnumerator()
            {
                return elements.GetEnumerator();
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
    }