Как правильно перебрать результаты поиска DMultiMap (DeCAL) в Delphi?

Я использую контейнер DMultiMap от DeCAL с Delphi 6 для хранения данных. Ключом является строка, которая может появляться на карте несколько раз.

Интересно, как правильно перебрать все объекты с заданным ключом.

Будет ли этот код:

function IterateOverObjects(map: DMultimap);
var iter: DIterator;
begin
  iter := map.locate(['abc']);
  while IterateOver(iter) do
  begin
    // do something with the value...
  end;
end;

возвращает все объекты с 'abc' в качестве ключа? Или он вернет все объекты карты, начиная с первого объекта с ключом 'abc'?

Изменить: Только что протестировано. Он возвращает все объекты карты, начиная с первого объекта с ключом 'abc'. Каков тогда лучший способ перебрать 'abc'?

3 ответа

Решение

РЕДАКТИРОВАТЬ: протестированная версия (я изменил ранее использованный findif, потому что я исследовал, что он не использует быстрый поиск, он просто просматривает все элементы):

РЕДАКТИРОВАТЬ 2: потому что мой предыдущий тест был неудачным, я отредактировал функцию, чтобы она работала правильно. это выглядит почти так же, как ответ Name, но я изменил его, чтобы никого не спутать с неправильной функцией.

    function IterateOverFound(Map: DMultiMap; var iter: DIterator; const obj: array of const): Boolean;
begin
  if diIteration in iter.flags then
  begin
    advance(iter);
    SetToKey(iter);
    iter := findIn(iter, Map.finish, obj);
  end
  else
  begin
    iter := Map.locate(obj);
    Include(iter.flags, diIteration);
  end;

  Result := not atEnd(iter);
  if not result then
    Exclude(iter.flags, diIteration);
end;

Пример использования:

var
  iter: DIterator;

  iter := map.start; 
  while IterateOverFound(map, iter, ['abc']) do
  begin
    SetToValue(iter);
    // get value
  end;

Мне нравится синтаксис примера использования, предложенного Линасом, но так как функция не работает должным образом, вот исправленная версия. Тот факт, что FindIn не использует быстрый поиск, не является проблемой, поскольку он используется только для итерации (DMultiMap - это упорядоченная карта, так что все элементы с одинаковым ключом находятся вместе):

function IterateOverFound(Map: DMultiMap; var iter: DIterator; const obj: array of const): Boolean;
var bWasToKey: boolean;
begin
  if diIteration in iter.flags then
  begin
    advance(iter);
    bWasToKey := diKey in iter.flags;
    SetToKey(iter);
    iter := DeCAL.findIn(iter, DeCAL.getContainer(iter).finish, obj);
    if not bWasToKey then
      SetToValue(iter);
  end else
  begin
    iter := Map.locate(obj);
    Include(iter.flags, diIteration);
  end;
  result := not atEnd(iter);
  if not result then
    Exclude(iter.flags, diIteration);
end;

Пример использования:

var
  map: DMultiMap;
  iter: DIterator;

map := DMultiMap.Create;
map.putPair(['aaa', 0]);
map.putPair(['def', 1]);
map.putPair(['abc', 2]);
map.putPair(['abc', 3]);
map.putPair(['def', 4]);
map.putPair(['abc', 5]);
map.putPair(['def', 6]);
iter := map.start;
while IterateOverFound(map, iter, ['abc']) do
begin
  // do something with the value...
end;

Тем временем я провел несколько исследований и нашел одно решение. Поскольку DMultiMap представляет собой упорядоченную карту (основанную на черном дереве, а не на хэш-значении), все элементы с одинаковым ключом сгруппированы так, чтобы работал следующий код:

function IterateOverObjects(map: DMultimap);
var iter1, iter2: DIterator;
begin
  iter1 := map.locate(['abc']);
  if not AtEnd(iter1) then
  begin
    iter2 := map.upper_bound(['abc']);
    repeat
      // do something with the value...
      Advance(iter1);
    until equals(iter1, iter2);
  end;
end;

Другая возможность будет:

function IterateOverObjects(map: DMultimap);
var iter: DIterator;
begin
  iter := map.locate(['abc']);
  while IterateOver(iter) do
  begin
    SetToKey(iter);
    if (getString(iter) <> 'abc') then break;
    SetToValue(iter);
    // do something with the value...
  end;
end;
Другие вопросы по тегам