2010-03-27 12 views
16

Come posso utilizzare un TEnumerator per scorrere il mio TDictionary in ordine per chiave?Come posso elencare un TDictionary in ordine alfabetico per chiave in Delphi 2009?

Ho qualcosa di simile:

var 
    Dic: TDictionary<string, string>; 
    Enum: TPair<string, string>; 

    begin 
    Dic := TDictionary<string, string>.create; 
    Dic.Add('Tired', 'I have been working on this too long'); 
    Dic.Add('Early', 'It is too early in the morning to be working on this'); 
    Dic.Add('HelpMe', 'I need some help'); 
    Dic.Add('Dumb', 'Yes I know this example is dumb'); 

    { I want to do the following but do it in sorted order by Enum.Key } 
    for Enum in Dic do 
     some processing with Enum.Key and Enum.Value; 

    Dic.Free; 
    end; 

quindi vorrei per elaborare il mio dizionario nell'ordine: Dumb, Early, HelpMe, Stanco.

Sfortunatamente l'aiuto di Delphi è molto minimale nel descrivere come funzionano in particolare gli enumeratori in generale e TEnumerator e non fornisce esempi che io possa trovare. C'è anche molto poco scritto sul web sull'uso degli Enumeratori con Generics in Delphi.

E il mio codice di esempio sopra non utilizza nemmeno TEnumerator, quindi sono confuso su come tutto questo è stato progettato per essere utilizzato.


Grazie Barry, per la risposta.

La mia avventura in Generics da quando ho posto la domanda era interessante. Volevo iniziare a implementarli nel mio codice. Il problema del "sorting" era un po 'perplesso, dal momento che sembra che Generics abbia dei metodi che hanno a che fare con l'ordinamento integrato, ma non ci sono buoni esempi o documentazione su come farlo.

Alla fine ho fatto quello che Barry ha suggerito e ho creato un indice esterno nel dizionario. Tuttavia, non sembra giusto.

Tuttavia, poi ho avuto un'altra sorpresa: stavo tentando di sostituire GPStringHashGabr con il TDictionary generico. Il codice era un po 'più pulito con i generici. Ma la linea di fondo era che TDictionary era oltre 3 volte più lento di quello di Gabr. 1.704.667 chiamate a TryGetValue hanno richiesto 45 secondi, ma la stessa operazione delle routine di Gabr ha richiesto 12 secondi. Non sono sicuro del perché, ma forse è semplice come Gabr che ha una funzione Hash più veloce e una combinazione di benna. O forse i generici hanno dovuto generalizzare per ogni caso e questo di per sé lo rallenta.

Mai-il-meno, forse Barry o gli altri sviluppatori Delphi dovrebbero guardare a questo, perché un aumento di velocità di 3 volte potrebbe in definitiva avvantaggiare tutti. Personalmente preferirei usare prima quello che è incorporato nella lingua rispetto a un pacchetto di terze parti (anche uno buono come quello di Gabr), se avessi la possibilità di scegliere. Ma per ora, mi limiterò a GPStringHash.

+0

Ecco un seguito: all'inizio di quest'anno (2016), ho eseguito l'aggiornamento a Delphi XE8. Stavo pensando che TDictionary nel pacchetto Delphi Generics potrebbe essere migliorato da quando ho fatto questa domanda 5 anni fa. Così ho estratto GPStringHash da @gabr e l'ho sostituito con TDictionary. Il rallentamento del mio programma è stato abbastanza significativo. Quindi, almeno per il prossimo futuro, mi sto attaccando con GPStringHash. – lkessler

+1

A seconda delle esigenze, anziché conservare i dati in un dizionario e occasionalmente ordinarli, è possibile fare il contrario: conservare i dati in un elenco ordinato, ma utilizzare un dizionario per l'accesso rapido "casuale". Questo si adatta meglio se si sta salvando/recuperando tramite SQL, ad esempio. Sii sempre chiaro chi possiede oggetti (archi, sei al sicuro). –

risposta

5

Nel mio caso, io uso il TDictionary < String, String>. classe TKeyCollection.

function compareKey(const L, R: String): Integer; 
begin 
    Result := SysUtils.CompareText(L, R); 
end; 

function getReverseSortedKeyArray(dictionary: TDictionary<String, String): TArray<String>; 
var 
    keyArray: TArray<String>; 
    keyCollecttion: TDictionary<String, String>.TKeyCollection; 
begin 
    keyCollecttion:= TDictionary<String, String>.TKeyCollection.Create(dictionary); 
    try 
    keyArray:= valueCollecttion.ToArray; 
    TArray.Sort<String>(keyArray, TComparer<String>.Construct(compareKey)); 
    finally 
    keyCollecttion.Free; 
    end; 

    Result := keyArray; 
end; 

Esempio di utilizzo:

var 
    key: String; 
    keyArray : TArray<String>; 
begin 
    keyArray := getSortedKeyArray (dictionary); 
    for key in keyArray do 
    begin 
     // ... 
    end; 
end; 
19

Il dizionario è una tabella hash, quindi non memorizza gli articoli in ordine. TEnumerator è semplice - è solo un mezzo per iterare sugli oggetti.

Per ottenere gli articoli in un ordine, è necessario ordinarli. Un modo potrebbe essere quello di metterli in una lista e ordinare l'elenco, in questo modo:

var 
    list: TList<string>; 
begin 
    list := TList<string>.Create(Dic.Keys); 
    try 
    list.Sort; 
    // process sorted list of items now 
    finally 
    list.Free; 
    end; 
end; 
+0

Beh, sì, è abbastanza semplice mantenere un TList separato come un indice ordinato delle chiavi. Ma in Generics.Collections c'è il metodo DoMoveNext di TEnumerator che dovrebbe definire l'ordinamento, e MoveNext e DoMoveNext (qual è la differenza?) Per consentire l'enumerazione delle raccolte. Questi non mi consentono di enumerare la collezione direttamente senza dover creare e mantenere un indice separato? Sono questi che sono spiegati molto male ovunque. – lkessler

+0

@Barry: p.s. Ho visto la tua risposta in: http://stackoverflow.com/questions/1230054/why-does-tenumerablet-use-pass-through-methods e sono felice che tu abbia scritto TEnumerable per le prestazioni prima. Sono uno di quelli il cui programma ha bisogno di ogni velocità possibile. Sto cercando ora di passare ai generici basati su hash table di Delphi 2009. – lkessler

+0

... e mentre approfondisco di più, vedo tutti quei confronti definiti in Generics.Defaults. Sembrano utili, ma a cosa servono se vogliamo solo mantenere un indice TList separato? – lkessler

4

Ecco un esempio di codice che ordina tramite Array<T> o un TList<T>. Conserva la relazione Coppia valore chiave e può anche essere ottimizzata per ordinare per valore anziché chiave. Inoltre, usa un metodo anonimo per fare l'ordinamento.

Assicurarsi di includere Generics.Collections e Generics.Defaults nella clausola uses. Il primo metodo per ordinare utilizzando TArray<T>:

procedure TestSortDictionaryViaArray; 
var 
    D: TDictionary<string, Integer>; 
    A: TArray<TPair<string, Integer>>; 
    P: TPair<string, Integer>; 
begin 
    D := TDictionary<string, Integer>.Create; 

    D.Add('Test - 6', 6); 
    D.Add('Test - 1', 1); 
    D.Add('Test - 0', 0); 
    D.Add('Test - 4', 4); 
    D.Add('Test - 3', 3); 
    D.Add('Test - 5', 0); 
    D.Add('Test - 2', 2); 

    A := D.ToArray; 

    TArray.Sort<TPair<string, Integer>>(A, 
    TComparer<TPair<string, Integer>>.Construct(
     function (const L, R: TPair<string, Integer>): Integer 
     begin 
     Result := CompareStr(L.Key, R.Key); 
     end) 
); 

    for P in A do 
    ShowMessage(P.Key); 
    D.Free; 
end; 

E questo sta usando TList<T>:

procedure TestSortDictionaryViaList; 
var 
    D: TDictionary<string, Integer>; 
    L: TList<TPair<string, Integer>>; 
    P: TPair<string, Integer>; 
begin 
    D := TDictionary<string, Integer>.Create; 

    D.Add('Test - 6', 6); 
    D.Add('Test - 1', 1); 
    D.Add('Test - 0', 0); 
    D.Add('Test - 4', 4); 
    D.Add('Test - 3', 3); 
    D.Add('Test - 5', 0); 
    D.Add('Test - 2', 2); 

    L := TList<TPair<string, Integer>>.Create(D); 

    L.Sort(
    TComparer<TPair<string, Integer>>.Construct(
     function (const L, R: TPair<string, Integer>): Integer 
     begin 
     Result := CompareStr(L.Key, R.Key); 
     end) 
); 

    for P in L do 
    ShowMessage(P.Key); 

    D.Free; 
    L.Free; 
end; 

aggiuntive (e inutili) informazioni: Il metodo TList<T> ha bisogno della lista di essere liberato, mentre il TArray<T> non ha bisogno di liberare. Internamente, TList<T> utilizza TArray<T> (ad esempio, TArray ha un metodo di classe BinarySearch() e TList<T> ha un metodo BinarySearch).

Problemi correlati