Как я могу перечислить TDictionary в алфавитном порядке по ключу в Delphi 2009?
Как я могу использовать TEnumerator для прохождения моего TDictionary в отсортированном порядке по ключу?
У меня есть что-то вроде этого:
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;
Поэтому я хотел бы обработать свой словарь в следующем порядке: тупой, ранний, HelpMe, усталый.
К сожалению, помощь Delphi очень минимальна в описании того, как работают перечислители в целом и TEnumerator, и не дает примеров, которые я могу найти. В Интернете также очень мало написано об использовании Enumerator s с Generics в Delphi.
И мой пример кода выше даже не использует TEnumerator, поэтому я не понимаю, как все это предназначено для использования.
Спасибо, Барри, за твой ответ.
С тех пор, как я задал вопрос, мое занятие дженериками было интересным Я хотел начать реализовывать их в своем коде. Проблема "сортировки" несколько озадачивала, поскольку, похоже, что в Generics, похоже, есть встроенные методы сортировки, но нет хороших примеров или документации о том, как это сделать.
В конце концов я сделал то, что предложил Барри, и встроил внешний словарь в словарь. Тем не менее, это не правильно.
Однако затем у меня был еще один сюрприз: я пытался заменить GPStringHash Габра универсальным TDictionary. Код был немного чище с дженериками. Но суть в том, что TDictionary был более чем в 3 раза медленнее, чем Габр. 1 704 667 вызовов в TryGetValue заняли 0,45 секунды, но та же операция, что и в процедурах Габра, заняла 0,12 секунды. Я не уверен, почему, но, возможно, это так же просто, как у Габра более быстрая комбинация хеш-функции и комбинации. Или, может быть, генерики должны были обобщать для каждого случая, и это по своей сути замедляет его.
Тем не менее, возможно, Барри или другим разработчикам Delphi следует обратить на это внимание, потому что ускорение в 3 раза может в конечном итоге принести пользу всем. Лично я бы скорее использовал то, что встроено в язык, чем сторонний пакет (даже такой же, как у Габра), если бы у меня был выбор. Но сейчас я буду придерживаться GPStringHash.
3 ответа
В моем случае я использую TDictionary
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;
Пример использования:
var
key: String;
keyArray : TArray<String>;
begin
keyArray := getSortedKeyArray (dictionary);
for key in keyArray do
begin
// ...
end;
end;
Словарь представляет собой хэш-таблицу, поэтому он не хранит элементы в отсортированном порядке. TEnumerator прост - это просто средство перебора элементов.
Чтобы получить предметы в заказе, вам необходимо отсортировать их. Один из способов - поместить их в список и отсортировать список следующим образом:
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;
Вот пример кода, который сортирует через Array<T>
или TList<T>
, Он сохраняет отношение пары "ключ-значение", и его также можно настроить для сортировки по значению вместо ключа. Также для сортировки используется анонимный метод.
Не забудьте включить Generics.Collections
а также Generics.Defaults
в вашем uses
пункт. Первый метод сортировки с использованием 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;
И это использует 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;
Дополнительная (и ненужная) информация: TList<T>
метод нуждается в освобождении списка, тогда как TArray<T>
не нуждается в освобождении. Внутренне TList<T>
использования TArray<T>
(например, TArray
имеет BinarySearch()
метод класса и TList<T>
имеет метод BinarySearch).