问题
i have this problem: starting from an empty list (0 elements) i want check if an element is present or not present in this list. In case this record not is present in list then i add this record to list, otherwise update element in list. I have tried writing this code:
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.
and should return this result:
10 2
20 1
30 1
40 1
50 1
but return this result:
10 1
20 1
30 1
40 1
50 1
10 1
I have understood about problem: when i use oDBStats.Contains(rDBStats) it control if rDBStats element is contained in list; the first time not found it and add in list; but when it is added in list i update freq field to 1; so second time when i check again being rdbstats with freq = 0 not found it. As i can solve this problem? I need to have a counter, where i get from input a "comb" and i want check if this "comb" is present in list, indipendetely from value of the other field of the record. In case i find "comb" in list, then i update, increasing freq field. Thanks for help.
回答1:
When you call Contains on a generic list, it looks if the given value is already inside the list. The value in your case is a record which consists of two fields. As you didn't specify a custom comparer, Delphi will use a default comparer which in case of a record does a binary compare. So only when two records are binary equal they will be treated as equal.
To make your example work you have to specify a custom comparer that compares only the comb field of the records. This is an example:
oDBStats := TList<TDBStats>.Create(TDelegatedComparer<TDBStats>.Create(
function(const Left, Right: TDBStats): Integer
begin
result := CompareValue(Left.comb, Right.comb);
end));
In addition you have an error in your update routine. Instead of incrementing the existing value, you are incrementing the undefined value of the item parameter. The change in the first line should make it work:
rItem := oDBStats[Index];
Inc(rItem.Freq);
oDBStats[Index] := rItem;
回答2:
You have the wrong data structure since what you really need is a dictionary.
The fundamental problem with using a list is that you want to search on a subset of the stored record. But lists are not set up for that. Solve the problem by re-writing using TDictionary<Integer, Integer>.
I can recommend that you have a thorough read of the dictionary code example at the Embarcadero docwiki.
The key to the dictionary is what you call comb and the value is freq. To add an item you do this:
if Dict.TryGetValue(Comb, Freq) then
Dict[Comb] := Freq+1
else
Dict.Add(Comb, 1);
I'm assuming your dictionary is declared like this:
var
Dict: TDictionary<Integer, Integer>;
and created like this:
Dict := TDictionary<Integer, Integer>;
You can enumerate the dictionary with a simple for in loop.
var
Item: TPair<Integer, Integer>;
...
for Item in Dict do
Writeln(Item.Key:3, Item.Value:10);
Although be warned that the dictionary will enumerate in an odd order. You may wish to sort before printing.
If you wish to store more information associated with each entry in the dictionary then put the additional fields in a record.
type
TDictValue = record
Freq: Integer;
Field1: string;
Field2: TDateTime;
//etc.
end;
Then your dictionary becomes TDictionary<Integer, TDictValue>.
来源:https://stackoverflow.com/questions/8862807/list-and-contains-method