List and Contains method

流过昼夜 提交于 2019-12-07 13:05:06

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!