Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert SortedList to IOrderedEnumerable [duplicate]

Tags:

c#

linq

I'm currently having trouble finding a way to get an IOrderedEnumerable from a SortedList.

I have a complex type, we'll just call it 'A' for now, that can be decomposed into an enumerable of type 'A'. I'm currently creating a SortedList in a recursive decomposition function where the key, an int, is related to the order the piece was decomposed in:

private static SortedList<int, A> RecursivelyBuildMySortedList(A myValue)
{
    if (myValue == StopCondition())
    {
        return new SortedList<int, A> { { 1, myValue } };
    }

    var left = RecursivelyBuildMySortedList(myValue.Left);
    var right = RecursivelyBuildMySortedList(myValue.Right).Select(entry => new KeyValuePair<int, A>(entry.Key + left.Count, entry.Value)).ToList();
    right.ForEach(pair => left.Add(pair.Key, pair.Value));
    return left;
}

However, I don't want to expose the SortedList to consumers since the value of the key, related to decomposition order, has little meaning to consumers (especially as an int). The only thing a consumer needs to care about is what the final ordering of the pieces was so that each piece can be processed in the correct order. I would prefer to expose an IOrderedEnumerable to consumers. I thought this would be a fairly simple task since a SortedList is in many ways very similar to an OrderedEnumerable, but I've not been able to find a good conversion yet:

public static IOrderedEnumerable<A> Decompose(A myValue)
{
    SortedList<int, A> mySortedList = RecursivelyBuildMySortedList(myValue);

    // Can't find a way to preserve the order of the objects during 'OrderBy'
    // mySortedList.Select(keyValuePair => keyValuePair.Value).OrderBy(obj => obj.index);

    // The select statement would not match the return type that promises IOrderedEnumerable
    // mySortedList.OrderBy(keyValuePair => keyValuePair.Key).Select(keyValuePair => keyValuePair.Value);

}

Does anyone have a method for extracting an IOrderedEnumerable from a SortedList for consumption? As a side note, I'm aware that:

return mySortedList.Select(keyValuePair => keyValuePair.Value);

Would return an IEnumerable that would preserve order 'under the hood', but due to how important the order in which the enumerable is processed is, I would rather the return type be descriptive enough to convey that the underlying collection is ordered (to make the API more readable).

like image 618
LiamK Avatar asked May 12 '15 19:05

LiamK


3 Answers

You need to use the Values propery of the SortedList:

  private IOrderedEnumerable<string> GetResults() {
     SortedList<int, string> list = new SortedList<int, string>();
     list.Add(40, "Mehrzad");
     list.Add(20, "Chehraz");
     return list.Values.OrderBy(key => 0);         
  }

And then use:

 IOrderedEnumerable<string> enumerable = GetResults();
 foreach (var item in enumerable) {
     System.Diagnostics.Debug.WriteLine(item);
 }

It works since OrderBy (key => 0) returns values in their original order which are sorted by the SortedList.

Or you may implement IOrderedEnumerable (My old answer):

class OrderedEnumerableWithoutKey<TKey, TValue> : IOrderedEnumerable<TValue> {
     private IOrderedEnumerable<KeyValuePair<TKey, TValue>> inner;
     public OrderedEnumerableWithoutKey(IOrderedEnumerable<KeyValuePair<TKey, TValue>> inner) {
        this.inner = inner;
     }
     public IOrderedEnumerable<TValue> CreateOrderedEnumerable<TKey1>(Func<TValue, TKey1> keySelector, IComparer<TKey1> comparer, bool descending) {
        throw new NotImplementedException();
     }
     public IEnumerator<TValue> GetEnumerator() {
        return new Enumerator(inner.GetEnumerator());
     }
     IEnumerator IEnumerable.GetEnumerator() {
        return new Enumerator(inner.GetEnumerator());
     }
     class Enumerator : IEnumerator<TValue> {
        private IEnumerator<KeyValuePair<TKey, TValue>> inner;
        public Enumerator(IEnumerator<KeyValuePair<TKey, TValue>> inner) {
           this.inner = inner;
        }
        public TValue Current {
           get {
              return inner.Current.Value;
           }
        }
        object IEnumerator.Current {
           get {
              return inner.Current.Value;
           }
        }
        public void Dispose() {
           this.inner.Dispose();
        }
        public bool MoveNext() {
           return this.inner.MoveNext();
        }
        public void Reset() {
           this.inner.Reset();
        }
     }
  }

Then use it in that way:

private IOrderedEnumerable<string> GetResults() {
     SortedList<int, string> list = new SortedList<int, string>();
     list.Add(20, "Mehrzad");
     list.Add(10, "Chehraz");
     return new OrderedEnumerableWithoutKey<int, string>(list.OrderBy(item => item.Key));    }
..
..
// Consumer part:
IOrderedEnumerable<string> enumerable = GetResults();
foreach (var item in enumerable) {
      System.Diagnostics.Debug.WriteLine(item);
}
// Outputs:
// Cherhaz
// Mehrzad
like image 73
Mehrzad Chehraz Avatar answered Nov 03 '22 16:11

Mehrzad Chehraz


There is no public implementation of IOrderedEnumerable available. You'll have to roll your own.

It is fairly simple to do with the help of Enumerable.OrderBy and Enumerable.OrderByDescending. With minimal possible code, see the implementation below.

public class SortedListOrderedEnumerable<TKey, TValue> : IOrderedEnumerable<TValue>
{
    private readonly SortedList<TKey, TValue> innerList;
    public SortedListOrderedEnumerable(SortedList<TKey, TValue> innerList)
    {
        this.innerList = innerList;
    }

    public IOrderedEnumerable<TValue> CreateOrderedEnumerable<TKey1>(Func<TValue, TKey1> keySelector, IComparer<TKey1> comparer, bool @descending)
    {
        return @descending 
            ? innerList.Values.OrderByDescending(keySelector, comparer) 
            : innerList.Values.OrderBy(keySelector, comparer);
    }

    public IEnumerator<TValue> GetEnumerator()
    {
        return innerList.Values.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public static class Ext
{
    public static IOrderedEnumerable<TValue> AsOrderedEnumerable<TKey, TValue>(
        this SortedList<TKey, TValue> list)
    {
        return new SortedListOrderedEnumerable<TKey, TValue>(list);
    } 
}

Then use it as

SortedList<int, string> list = new SortedList<int, string>();
...
var ordered = list.AsOrderedEnumerable()
    .ThenBy(...)
    .ThenByDescending(...);

Now your Decompose method will become

public static IOrderedEnumerable<A> Decompose(A myValue)
{
    SortedList<int, A> mySortedList = RecursivelyBuildMySortedList(myValue);        
    return mySortedList.AsOrderedEnumerable();
}

Edit: Updated my initial answer to return IOrderedEnumerable<TValue>, in original answer it was IOrderedEnumerable<KeyValuePair<TKey,TValue>>

like image 37
Sriram Sakthivel Avatar answered Nov 03 '22 16:11

Sriram Sakthivel


You likely don't want to use IOrderedEnumerable for this.

IOrderedEnumerable exists solely for things like ThenBy(...) where you're dynamically specifying the sort order at run time (really, just building a comparer).

SortedList on the other hand has a constant sort order and comparer -- its ordering can not change.

I suggest sticking to plain IEnumerable unless you've got a good reason you haven't yet stated. Remember, interfaces aren't there to document a type (i.e. by saying "this is ordered"). They're there to expose functionality.

like image 1
Cory Nelson Avatar answered Nov 03 '22 16:11

Cory Nelson