我有这个问题:从一个空列表(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没找到它。 我可以解决这个问题吗?我需要一个计数器,我从输入中得到一个“梳子”,我想检查这个“梳子”是否存在于列表中,独立于记录的另一个字段的值。如果我在列表中找到“梳子”,那么我更新,增加频率字段。 谢谢你的帮助。
答案 0 :(得分:6)
当您在通用列表上调用Contains
时,它会查看给定值是否已在列表中。您的案例中的值是一个由两个字段组成的记录。由于您没有指定自定义比较器,Delphi将使用默认比较器,在记录的情况下进行二进制比较。因此,只有当两个记录是二进制相等时,它们才会被视为相等。
要使您的示例正常工作,您必须指定一个自定义比较器,仅比较记录的梳理字段。这是一个例子:
oDBStats := TList<TDBStats>.Create(TDelegatedComparer<TDBStats>.Create(
function(const Left, Right: TDBStats): Integer
begin
result := CompareValue(Left.comb, Right.comb);
end));
此外,您的更新例程中出现错误。您正在递增item参数的未定义值,而不是递增现有值。第一行的变化应该能够起作用:
rItem := oDBStats[Index];
Inc(rItem.Freq);
oDBStats[Index] := rItem;
答案 1 :(得分:1)
您的数据结构错误,因为您真正需要的是dictionary。
使用列表的基本问题是您要搜索存储记录的子集。但是没有为此设置列表。通过使用TDictionary<Integer, Integer>
重写来解决问题。
我建议您仔细阅读dictionary code example at the 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>
。