Как правильно перебрать результаты поиска DMultiMap (DeCAL) в Delphi? - PullRequest
2 голосов
/ 18 февраля 2011

Я использую контейнер 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 ]

1 голос
/ 18 февраля 2011

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

EDIT2: поскольку мой предыдущий тест был неудачным, я отредактировал функцию, чтобы она работала правильно. это выглядит почти так же, как ответ 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;
1 голос
/ 21 февраля 2011

Мне нравится синтаксис примера использования, предложенного Линасом, но так как функция не работает должным образом, вот исправленная версия.Тот факт, что 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;
1 голос
/ 18 февраля 2011

Тем временем я провел несколько исследований и нашел одно решение.Поскольку 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;
...