2015-05-12 3 views
6

Attualmente non riesco a trovare un modo per ottenere un oggetto IOrderedEnumerable da SortedList.Convert SortedList to IOrderedEnumerable

Ho un tipo complesso, lo chiameremo semplicemente 'A' per ora, che può essere scomposto in un numero enumerabile di tipo 'A'. Attualmente sto creando un SortedList in una funzione di decomposizione ricorsiva in cui la chiave, un int, è legato alla fine il pezzo è stato scomposto 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; 
} 

Tuttavia, non voglio esporre il SortedList per i consumatori poiché il valore della chiave, correlato all'ordine di decomposizione, ha poco significato per i consumatori (specialmente come int). L'unica cosa di cui un consumatore deve preoccuparsi è l'ordine finale dei pezzi in modo che ogni pezzo possa essere elaborato nell'ordine corretto. Preferirei esporre un oggetto IOrderedEnumerable ai consumatori. Ho pensato che questo sarebbe stato un compito abbastanza semplice dal momento che un SortedList è per molti versi molto simile ad un OrderedEnumerable, ma io non sono stato in grado di trovare una buona conversione ancora:

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); 

} 

Qualcuno ha un metodo per l'estrazione un IOrderedEnumerable da SortedList per il consumo? Come nota a margine, mi rendo conto che:

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

sarebbe tornato un IEnumerable che mantenere l'ordine 'sotto il cofano', ma a causa di quanto sia importante l'ordine in cui l'enumerabile viene elaborato è, avrei preferito il tipo di ritorno è abbastanza descrittivo da comunicare che la raccolta sottostante è ordinata (per rendere l'API più leggibile).

+1

Perché deve essere _ coperto in un 'IOrderedEnumerable <>'? Non ci dovrebbe davvero essere alcun bisogno. Sai 'SortedList <,>' è ordinato ... –

+0

Penso che dovresti andare con 'IEnumerable' e aggiungere un commento alla funzione come il risultato è ordinato. Restituire "IOrderedEnumerable" in realtà non dice al consumatore nulla su come le collezioni siano ordinate comunque. – Magnus

+0

@JeffMercado, perché l'esposizione della chiave utilizzata per ordinare gli elementi è priva di significato per i consumatori e speravo di ripulire la mia presentazione del risultato. Per esprimere la domanda in un modo diverso, se 'Decompose' era una funzione che prime considerava un intero e restituiva i fattori nell'ordine in cui erano stati scoperti, la risultante 'chiave' nella lista ordinata (l'ordine in cui era stato scoperto il fattore) , sarebbe privo di significato una volta ottenuto il risultato. – LiamK

risposta

2

È necessario utilizzare i valori nella proprietà di 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);   
    } 

e quindi utilizzare:

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

Funziona dal OrderBy (key => 0) restituisce i valori nel loro ordine originale che sono ordinati dalla SortedList.

Oppure si può implementare IOrderedEnumerable (La mia vecchia risposta):

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(); 
     } 
    } 
    } 

quindi utilizzarlo in questo modo:

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 
+0

Il tuo 'OrderedEnumerableWithoutKey' esploderà quando' GetResults(). ThenB (..) 'viene chiamato. Non è una cosa intuitiva da chiamare. E 'list.Values.OrderBy (chiave => 0)' sta ordinando la lista che è già stata ordinata. Non è un problema di per sé; ma potrebbe essere inefficiente se la lista ordinata è enorme. –

+0

@SriramSakthivel Sono d'accordo sul fatto che le mie risposte potrebbero non essere le migliori, e lo rimuoverò se fosse trovata una risposta migliore. –

+0

Spero che il mio sia migliore: p –

2

Non v'è alcuna implementazione pubblica del IOrderedEnumerable disponibili. Dovrai arrotolare il tuo.

È abbastanza semplice fare con l'aiuto di Enumerable.OrderBy e Enumerable.OrderByDescending. Con il minimo codice possibile, vedere l'implementazione di seguito.

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); 
    } 
} 

quindi utilizzarlo come

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

Ora il metodo di Decompose diventerà

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

Modifica: Aggiornato la mia risposta iniziale per tornare IOrderedEnumerable<TValue>, in risposta originale era IOrderedEnumerable<KeyValuePair<TKey,TValue>>

+0

Pulire l'impianto. @MehrzadChehraz è stato in grado di dare una risposta che non mi richiedesse di implementare la mia interfaccia, ma anche questo era adatto. – LiamK

+0

@LiamK Sì. Ma questo è inefficiente in quanto ordina una lista che è già stata ordinata. Per le piccole liste questo va bene; Se la lista è enorme, non lo è. A seconda di ciò, puoi decidere di usarlo. –

+0

Punto giusto. Sono andato con l'altra risposta perché l'enumerabile risultante conterrà sempre solo tra 1 e 3 elementi. – LiamK

1

Probabilmente non vuoi usare IOrderedEnumerable per questo.

IOrderedEnumerable esiste solo per le cose come ThenBy(...) in cui si specifica dinamicamente l'ordinamento in fase di esecuzione (in realtà, solo la creazione di un confronto).

SortedList d'altra parte ha un ordinamento e un confronto costante - il suo ordine non può cambiare.

Suggerisco di attenersi al numero IEnumerable a meno che non si abbia una buona ragione che non ha ancora dichiarato. Ricorda, le interfacce non sono lì per documentare un tipo (cioè dicendo "questo è ordinato"). Sono lì per esporre funzionalità.