¿Cómo puedo listar un TDictionary en orden alfabético por clave en Delphi 2009?
generics delphi-2009 (3)
Aquí hay un código de ejemplo que se ordena a través de Array<T>
o un TList<T>
. Preserva la relación del par de valores clave, y también se puede modificar para que se ordene por valor en lugar de clave. Además, utiliza un método anónimo para hacer la clasificación.
Asegúrese de incluir Generics.Collections
y Generics.Defaults
en su cláusula de uses
. El primer método para ordenar usando 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;
Y esto está 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;
Información adicional (e innecesaria): el TList<T>
necesita que se libere la lista, mientras que el TArray<T>
no necesita ser liberado. Internamente, TList<T>
usa TArray<T>
(por ejemplo, TArray
tiene un método de clase BinarySearch()
, y TList<T>
tiene un método de BinarySearch).
¿Cómo puedo usar un TEnumerator para revisar mi TDictionary en orden ordenado por clave?
Tengo algo como esto:
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;
Así que me gustaría procesar mi diccionario en el orden: Dumb, Early, HelpMe, Tired.
Desafortunadamente, la ayuda de Delphi es muy mínima para describir cómo los enumeradores en general y TEnumerator funcionan específicamente y no dan ejemplos que pueda encontrar. También hay muy poco escrito en la web sobre el uso de Enumerators con Genéricos en Delphi.
Y mi código de ejemplo anterior ni siquiera usa TEnumerator, por lo que estoy confundido en cuanto a cómo está diseñado todo esto para ser usado.
Gracias Barry, por tu respuesta.
Mi incursión en genéricos desde que hice la pregunta fue interesante. Quería empezar a implementarlas en mi código. El problema de "clasificación" fue un tanto desconcertante, ya que parece que los genéricos parecen tener métodos relacionados con la clasificación incorporada, pero no hay buenos ejemplos o documentación sobre cómo hacerlo.
Al final hice lo que Barry sugirió y construí un índice externo en el Diccionario. Aún así, no se siente bien.
Sin embargo, luego tuve otra sorpresa: estaba intentando reemplazar el GPStringHash Gabr con el GPStringHash del genérico. El código fue un poco más limpio con los genéricos. Pero la conclusión era que TDictionary era 3 veces más lento que el de Gabr. 1,704,667 llamadas a TryGetValue tomaron .45 segundos, pero la misma operación a las rutinas de Gabr tomó .12 segundos. No estoy seguro de por qué, pero tal vez sea tan simple como que Gabr tenga una combinación de funciones Hash y agrupación más rápidas. O tal vez los genéricos debían generalizarse para cada caso y eso lo retrasa de forma inherente.
Sin embargo, quizás Barry o los otros desarrolladores de Delphi deberían ver esto, porque una aceleración de 3 veces podría beneficiar a todos. Personalmente, antes utilizaría lo que está integrado en el idioma que un paquete de terceros (incluso uno tan bueno como el de Gabr) si tuviera la opción. Pero por ahora, me quedo con GPStringHash.
El diccionario es una tabla hash, por lo que no almacena elementos en orden ordenado. TEnumerator es simple: es solo un medio de iterar sobre los elementos.
Para obtener artículos en un pedido, necesita ordenarlos. Una forma sería ponerlos en una lista y ordenar la lista, de esta manera:
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;
En mi caso, uso el TDictionary <String, String>. Clase 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>;
keyCollection: TDictionary<String, String>.TKeyCollection;
begin
keyCollection:= TDictionary<String, String>.TKeyCollection.Create(dictionary);
try
keyArray:= keyCollection.ToArray;
TArray.Sort<String>(keyArray, TComparer<String>.Construct(compareKey));
finally
keyCollection.Free;
end;
Result := keyArray;
end;
Ejemplo de uso:
var
key: String;
keyArray : TArray<String>;
begin
keyArray := getSortedKeyArray (dictionary);
for key in keyArray do
begin
// ...
end;
end;