Elenco e Contiene il metodo
-
28-10-2019 - |
Domanda
ho questo problema:a partire da una lista vuota (0 elementi) voglio verificare se un elemento è presente o non presente in questo elenco.Nel caso In cui questo record non è presente nell'elenco, quindi aggiungo questo record alla lista, altrimenti aggiornamento elemento in elenco.Ho provato a scrivere questo codice:
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.
e dovrebbe tornare questo risultato:
10 2
20 1
30 1
40 1
50 1
ma restituisce questo risultato:
10 1
20 1
30 1
40 1
50 1
10 1
Ho capito il problema:quando uso oDBStats.Contiene(rDBStats) per il controllo di se rDBStats elemento è contenuto in una lista;il primo tempo non l'ha trovato e aggiungere in lista;ma quando viene aggiunto nella lista che aggiorno la freq campo 1;quindi secondo tempo quando posso controllare ancora una volta di essere rdbstats con freq = 0 non trovato.Come posso risolvere questo problema?Ho bisogno di avere un contatore, dove mi da l'input di un "pettine" e voglio verificare se questo "pettine" è presente nella lista, indipendetely dal valore di un altro campo del record.Nel caso In cui trovo "pettine" in lista, poi ho aggiornato i, aumentando la freq campo.Grazie per l'aiuto.
Soluzione
Quando chiami Contains
In un elenco generico, sembra se il valore dato è già all'interno dell'elenco. Il valore nel tuo caso è un record composto da due campi. Poiché non hai specificato un confronto personalizzato, Delphi utilizzerà un confronto predefinito che in caso di record esegue un confronto binario. Quindi solo quando due record sono uguali, saranno trattati come uguali.
Per far funzionare il tuo esempio devi specificare un confronto personalizzato che confronta solo il campo di pettine dei record. Questo è un esempio:
oDBStats := TList<TDBStats>.Create(TDelegatedComparer<TDBStats>.Create(
function(const Left, Right: TDBStats): Integer
begin
result := CompareValue(Left.comb, Right.comb);
end));
Inoltre, hai un errore nella routine di aggiornamento. Invece di incrementare il valore esistente, si sta incrementando il valore non definito del parametro elemento. La modifica nella prima riga dovrebbe farlo funzionare:
rItem := oDBStats[Index];
Inc(rItem.Freq);
oDBStats[Index] := rItem;
Altri suggerimenti
Hai sbagliato i dati di struttura, in quanto ciò che si ha realmente bisogno è un dizionario.
Il problema fondamentale con l'utilizzo di un elenco che si desidera cercare in un sottoinsieme della stored record.Ma le liste non sono che.Risolvere il problema da ri-scrittura con TDictionary<Integer, Integer>
.
Posso consigliare di avere un'approfondita lettura del dizionario esempio di codice all'Embarcadero docwiki.
La chiave per il dizionario è quello che si chiama comb
e il valore è freq
.Per aggiungere un elemento, questo:
if Dict.TryGetValue(Comb, Freq) then
Dict[Comb] := Freq+1
else
Dict.Add(Comb, 1);
Sto supponendo che il tuo dizionario è dichiarato come questo:
var
Dict: TDictionary<Integer, Integer>;
e creato come questo:
Dict := TDictionary<Integer, Integer>;
È possibile enumerare il dizionario con un semplice for in
loop.
var
Item: TPair<Integer, Integer>;
...
for Item in Dict do
Writeln(Item.Key:3, Item.Value:10);
Anche se ha avvertito che il dizionario verrà enumerare in uno strano ordine.Si possono ordinare prima della stampa.
Se si desidera memorizzare più informazioni associate ad ogni voce del dizionario poi mettere le ulteriori campi di un record.
type
TDictValue = record
Freq: Integer;
Field1: string;
Field2: TDateTime;
//etc.
end;
Quindi il tuo dizionario è TDictionary<Integer, TDictValue>
.