Список и Содержит метод - PullRequest
3 голосов
/ 14 января 2012

У меня есть такая проблема: начиная с пустого списка (0 элементов), я хочу проверить, присутствует ли элемент в этом списке или нет. Если эта запись отсутствует в списке, я добавляю эту запись в список, в противном случае обновляю элемент в списке. Я пытался написать этот код:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Generics.Collections, System.Generics.Defaults;

type
  TDBStats = record
    Comb: Integer;
    Freq: Integer;
  end;
  TDBStatsList = TList<TDBStats>;

procedure Add(ODBStats: TDBStatsList; const Item: TDBStats);
var
  rItem: TDBStats;
begin
  rItem := Item;
  rItem.Freq := 1;
  oDBStats.Add(rItem);
end;

procedure Update(ODBStats: TDBStatsList; const Item: TDBStats; const Index: Integer);
var
  rItem: TDBStats;
begin
  rItem := Item;
  Inc(rItem.Freq);
  oDBStats[Index] := rItem;
end;


var
  oDBStats: TDBStatsList;
  rDBStats: TDBStats;
  myArr: array [0..4] of integer;
  iIndex1: Integer;
begin
  try
    myArr[0] := 10;
    myArr[1] := 20;
    myArr[2] := 30;
    myArr[3] := 40;
    myArr[4] := 10;

    oDBStats := TList<TDBStats>.Create;
    try
      for iIndex1 := 0 to 4 do
      begin
        rDBStats.Comb := myArr[iIndex1];
        if oDBStats.Contains(rDBStats) then
          Update(oDBStats, rDBStats, oDBStats.IndexOf(rDBStats))
        else
          Add(oDBStats, rDBStats);
      end;
      // Check List
      for iIndex1 := 0 to Pred(oDBStats.Count) do
        Writeln(oDBStats[iIndex1].Comb:3, oDBStats[iIndex1].Freq:10);
    finally
      oDBStats.Free;
    end;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

и должен вернуть этот результат:

10     2
20     1
30     1
40     1
50     1

но верните этот результат:

10     1
20     1
30     1
40     1
50     1
10     1

Я понял проблему: когда я использую oDBStats.Contains (rDBStats), он контролирует, содержится ли элемент rDBStats в списке; первый раз не нашел его и добавил в список; но когда он добавлен в список, я обновляю поле freq до 1; так что второй раз, когда я проверяю снова, будучи rdbstats с freq = 0, не нашел его. Как я могу решить эту проблему? Мне нужно иметь счетчик, где я получаю от ввода «гребень», и я хочу проверить, присутствует ли этот «гребень» в списке, независимо от значения другого поля записи. В случае, если я нахожу «гребень» в списке, я обновляю, увеличивая поле freq. Спасибо за помощь.

Ответы [ 2 ]

6 голосов
/ 14 января 2012

Когда вы вызываете Contains в общем списке, он смотрит, находится ли данное значение уже внутри списка.Значение в вашем случае - это запись, которая состоит из двух полей.Поскольку вы не указали пользовательский компаратор, Delphi будет использовать компаратор по умолчанию, который в случае записи выполняет двоичное сравнение.Таким образом, только когда две записи двоично равны, они будут рассматриваться как равные.

Чтобы ваш пример работал, вы должны указать пользовательский компаратор, который сравнивает только поле гребня записей.Это пример:

oDBStats := TList<TDBStats>.Create(TDelegatedComparer<TDBStats>.Create(
 function(const Left, Right: TDBStats): Integer
 begin
   result := CompareValue(Left.comb, Right.comb);
 end));

Кроме того, у вас есть ошибка в вашей процедуре обновления.Вместо того, чтобы увеличивать существующее значение, вы увеличиваете неопределенное значение параметра элемента.Изменение в первой строке должно заставить его работать:

  rItem := oDBStats[Index];
  Inc(rItem.Freq);
  oDBStats[Index] := rItem;
1 голос
/ 14 января 2012

У вас неправильная структура данных, так как вам действительно нужен словарь .

Основная проблема с использованием списка состоит в том, что вы хотите искать в подмножестве сохраненной записи. Но списки не созданы для этого. Решите проблему, переписав с помощью TDictionary<Integer, Integer>.

Я могу порекомендовать вам внимательно прочитать пример кода словаря на Embarcadero docwiki .

Ключ к словарю - это то, что вы называете comb, а значение freq. Чтобы добавить предмет, вы делаете это:

if Dict.TryGetValue(Comb, Freq) then
  Dict[Comb] := Freq+1
else
  Dict.Add(Comb, 1);

Я предполагаю, что ваш словарь объявлен так:

var
  Dict: TDictionary<Integer, Integer>;

и создан так:

Dict := TDictionary<Integer, Integer>;

Вы можете перечислить словарь с помощью простого цикла for in.

var
  Item: TPair<Integer, Integer>;
...
  for Item in Dict do
    Writeln(Item.Key:3, Item.Value:10);

Хотя и предупреждаем, что словарь будет перечислять в нечетном порядке. Вы можете сортировать перед печатью.

Если вы хотите сохранить больше информации, связанной с каждой записью в словаре, добавьте дополнительные поля в запись.

type
  TDictValue = record
    Freq: Integer;
    Field1: string;
    Field2: TDateTime;
    //etc.
  end;

Тогда ваш словарь станет TDictionary<Integer, TDictValue>.

...