Как правильно перебрать результаты поиска 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;