0
votes

Is there a safe way to mutate the value of a tdictionary, while iterating?

The first naive try:

var p: tpair<keytype,valuetype>;

begin
  for p in coll do
    // p.value is readonly for valuetypes and their fields.
 end;

failed, and also wrapping the valuetype in a RECORD doesn't help.

Iterating over keys and using dosetvalue might work, but it is private only.

I could of course use a reference type, but that seems a bit silly to me, since the state is an integer. Not elegant.

Added, complete sample:

program gendicttest;
//  tests if you can set valuetype key.

{$APPTYPE CONSOLE}

uses
  generics.collections;

type
  TCycleList = tdictionary<integer,integer>; // the value a record type doesn't work either.

var cyclelist :TCycleList;
    p : tpair<integer,integer>;
    j:integer;
begin
  cyclelist:=tcyclelist.Create;
  cyclelist.Add(3,4);
  for  p in cyclelist do
    writeln(p.Key, ' ',p.Value);

  if cyclelist.TryGetValue(3,j) then
    cyclelist.AddOrSetValue(3,j+1);
  for  p in cyclelist do
   p.Value:=0;     // <-- here, and alternative to do this.r

  for  p in cyclelist do
    writeln(p.Key, ' ',p.Value);    
end.
1
Please provide an minimal reproducible example, specifically including the definition of coll, keytype and valuetypeDsm
I added the testcode.Marco van de Voort
Maybe like cyclelist.AddOrSetValue(p.Key, 0);nil
That is the only thing I came up with too, but addorsetvalue can add values, invalidating the iterator. To mutate existing items, addorsetvalue calls the already mentioned dosetvalue, but that one is private.Marco van de Voort
Then you can saveguard that with Contains. Or maybe just clyclelist[p.Key] := 0; because TDictionary has a convenient default indexed property for reading and writing values: property Items[const Key: TKey]: TValue read GetItem write SetItem; default;nil

1 Answers

1
votes

Of course it won't work! The pair returned is a copy. So you can at best modify the copy, which makes no sense.

If you only want to clear the values, then get a copy of the keys and enumerate over them to set the value of each key to 0.

for var I in cycleList.Keys do
  cycleList.AddOrSetValue(I, 0);

Alternatively, you can use Items, which can be indexed with a key:

for var I in cycleList.Keys do
  cycleList.Items[I] := 0;

note: can also be done without Rio syntax