Как я могу перечислить TDictionary в алфавитном порядке по ключу в Delphi 2009? - PullRequest
18 голосов
/ 27 марта 2010

Как я могу использовать 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, и не дает примеров, которые я могу найти. В Интернете также очень мало написано об использовании перечислителей с обобщениями в Delphi.

И мой пример кода выше даже не использует TEnumerator, поэтому я не понимаю, как все это предназначено для использования.


Спасибо, Барри, за ответ.

Мое занятие дженериками, так как я задал вопрос, было интересным. Я хотел начать реализовывать их в своем коде. Проблема «сортировки» была несколько озадачивающей, поскольку кажется, что в Generics, похоже, есть встроенные методы сортировки, но нет хороших примеров или документации о том, как это сделать.

В конце концов я сделал то, что предложил Барри, и встроил внешний словарь в словарь. Тем не менее, это не правильно.

Однако затем у меня был еще один сюрприз: я пытался заменить Gabr GPStringHash на родословную TDictionary. Код был немного чище с дженериками. Но суть в том, что TDictionary был более чем в 3 раза медленнее, чем Габр. 1 704 667 вызовов в TryGetValue заняли 0,45 секунды, но та же операция, что и в процедурах Габра, заняла 0,12 секунды. Я не уверен, почему, но, возможно, это так же просто, как у Габра более быстрая комбинация функций хэша и группирования. Или, может быть, дженерики должны были обобщать для каждого случая, и это по сути замедляет его.

Тем не менее, возможно, Барри или другим разработчикам Delphi стоит взглянуть на это, потому что ускорение в 3 раза может в конечном итоге принести пользу всем. Лично я бы скорее использовал то, что встроено в язык, чем сторонний пакет (даже такой же, как у Габра), если бы у меня был выбор. Но сейчас я буду придерживаться GPStringHash.

Ответы [ 3 ]

21 голосов
/ 27 марта 2010

Словарь представляет собой хеш-таблицу, поэтому он не хранит элементы в отсортированном порядке. 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;
6 голосов
/ 11 апреля 2013

В моем случае я использую TDictionary . TKeyCollection class.

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;
4 голосов
/ 18 апреля 2013

Вот пример кода, который сортируется по 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).

...