Recent

Author Topic: accessing array members in dictionary  (Read 1665 times)

Weiss

  • Full Member
  • ***
  • Posts: 209
accessing array members in dictionary
« on: April 18, 2025, 05:37:57 am »
Say, I have dictionary specialized to <string, someObject>. Object, among other things, has array which I need to acceess and manipulate. What I was trying to do is this
Code: Pascal  [Select][+][-]
  1. someVariable:=myDictionary[someKey].value.array[i]
  2.  

Of course dictionary is created, and key-value pairs added, and each value, since they are instances of class, are created before becoming part of dictionary pair. The trouble is in accessing individual array members. I can assign that array, as a whole, to another array, and access array members, like so
Code: Pascal  [Select][+][-]
  1. myArray:=myDictionary[someKey].value.array;
  2. someVariable:=myArray[i];
  3.  

But this is not a solution, for variety of reasons. I am thinking, either my syntax is incorrect or maybe I should slowly drift away from dictionaries. I am writing from cell phone, code above is totally made up just to demonstrate what I mean.

Weiss

  • Full Member
  • ***
  • Posts: 209
Re: accessing array members in dictionary
« Reply #1 on: April 18, 2025, 05:57:08 am »
..also, when I say "object" I mean instance of class kind of object

TRon

  • Hero Member
  • *****
  • Posts: 4369
Re: accessing array members in dictionary
« Reply #2 on: April 18, 2025, 06:49:27 am »
I do not fully grasp what the issue actually is.

If I understood correctly what you wrote then it looks to me that this has nothing to do with TDictionary but rather with how to access the array that is stored inside the object. It only happens to be that the object is stored inside a dictionary.

Therefor I am unable to tell anything what might be able to help because the layout of the object is unknown to us.

A dictionary allows to store items in a list which can be accessed rapidly by using/searching/finding the key that was used. In your case it associates the string (key) with the object/class (item/value).
Today is tomorrow's yesterday.

cdbc

  • Hero Member
  • *****
  • Posts: 2137
    • http://www.cdbc.dk
Re: accessing array members in dictionary
« Reply #3 on: April 18, 2025, 07:18:16 am »
Hi
MeThinks it has to do with, how you access the array within the /object/...
If it's published via a property, then you can't access each element specifically, but have to assign it to a intermediary variable first.
So, either make getter/setter for each array-element and make it 'default', like 'Items' in a 'FPList', or make the array a public variable of the object, NO property business... Maybe, just maybe a property that exposes the array as a pointer to it, could work... I dunno  %)
Well, anyway - there's my 'Nickle's worth'
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

TRon

  • Hero Member
  • *****
  • Posts: 4369
Re: accessing array members in dictionary
« Reply #4 on: April 18, 2025, 07:30:44 am »
Taking a wild guess with following example but perhaps can help ?
Code: Pascal  [Select][+][-]
  1. program example;
  2.  
  3. {$mode objfpc}{$h+}
  4.  
  5. uses
  6.   types, sysutils, Generics.Collections;
  7.  
  8. type
  9.   TMyTestClass = class
  10.     Name: string;
  11.     Data: TIntegerDynArray;
  12.   end;
  13.  
  14.   TStringClassDictionary = specialize TDictionary<string, TMyTestClass>;
  15.  
  16. var
  17.   MyDictionary : TStringClassDictionary;
  18.   MyClass      : TMyTestClass;
  19.   i,j,k, x     : integer;
  20.  
  21. begin
  22.   MyDictionary := TStringClassDictionary.Create;
  23.   try
  24.     for i := 0 to 5 do
  25.     begin
  26.        MyClass := TMyTestClass.Create;
  27.        MyClass.Name := 'Something';
  28.        SetLength(MyClass.Data, 3);
  29.  
  30.        k := random(1000);
  31.  
  32.        for j := low(MyClass.data) to high(MyClass.data)
  33.          do MyClass.data[j] := k + j;
  34.  
  35.        MyDictionary.Add('item ' + i.ToString, MyClass);
  36.     end;
  37.  
  38.     // enumerate values
  39.     for MyClass in MyDictionary.Values do
  40.     begin
  41.       writeln(MyClass.Name, ' -> lowest value : ', MyClass.Data[low(MyClass.Data)]);
  42.     end;
  43.  
  44.     // Set random values to data (or do other things)
  45.     for MyClass in MyDictionary.Values do
  46.     begin
  47.       x := MyClass.Data[low(MyClass.Data)];
  48.       MyClass.Data[low(MyClass.Data)] := x * 10;
  49.     end;
  50.  
  51.     // verification
  52.     for MyClass in MyDictionary.Values do
  53.     begin
  54.       writeln(MyClass.Name, ' -> lowest value : ', MyClass.Data[low(MyClass.Data)]);
  55.     end;
  56.  
  57.   finally
  58.     for MyClass in MyDictionary.Values
  59.       do MyClass.Free;
  60.  
  61.     MyDictionary.Free;
  62.   end;
  63. end.
  64.  

If that is still too much work then you could consider using a pointer to the data array.
Today is tomorrow's yesterday.

Thaddy

  • Hero Member
  • *****
  • Posts: 16945
  • Ceterum censeo Trump esse delendam
Re: accessing array members in dictionary
« Reply #5 on: April 18, 2025, 08:23:58 am »
Too much work, TRon  :D :
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}
  2. uses generics.collections;
  3. type
  4.   TSomeclass = class
  5.   public somearray:array[0..3] of integer;
  6.   end;
  7.   TmyDictionary = specialize TObjectDictionary<integer,TSomeClass>;
  8. //someVariable:=myDictionary[someKey].value.somearray[i]  (Weiss)
  9. var
  10.   myDictionary:TmyDictionary;
  11.   a,b,c,d:TSomeclass;
  12. begin
  13.   a:=Tsomeclass.create;
  14.   b:=Tsomeclass.create;
  15.   c:=Tsomeclass.create;
  16.   d:=Tsomeclass.create;
  17.   a.somearray :=[1,2,3,4];
  18.   b.somearray :=[5,6,7,8];
  19.   c.somearray :=[9,10,11,12];
  20.   d.somearray :=[13,14,15,16];
  21.   myDictionary := TmyDictionary.create;
  22.   try
  23.     myDictionary.Add(1,a);
  24.     myDictionary.Add(2,b);
  25.     myDictionary.Add(3,c);
  26.     myDictionary.Add(4,d);
  27.     writeln(myDictionary[1].SomeArray[3]);// items is default
  28.   finally
  29.     myDictionary.Free;  // owns objects by default
  30.   end;
  31. {$ifdef mswindows}readln;{$endif}
  32. end.
@Weiss, you were on the right track, but you should have used TObjectDictionary.
If you posted this with a phone it is either really big or you have really small fingers. I could not do that.
@TRon I was writing this example while you posted so posts crossed. Your example is equally good.
« Last Edit: April 18, 2025, 08:57:06 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

TRon

  • Hero Member
  • *****
  • Posts: 4369
Re: accessing array members in dictionary
« Reply #6 on: April 18, 2025, 08:05:23 pm »
Too much work, TRon  :D
Not that it is /that/ surprising to see generics having a objectdictionary but omg and :facepalm: I missed that one  :-[

That is far more suited for the situation. Thank you for the example Thaddy.
Today is tomorrow's yesterday.

Weiss

  • Full Member
  • ***
  • Posts: 209
Re: accessing array members in dictionary
« Reply #7 on: April 20, 2025, 12:28:32 am »
Too much work...
@Weiss, you were on the right track, but you should have used TObjectDictionary.
If you posted this with a phone it is either really big or you have really small fingers. I could not do that.
@TRon I was writing this example while you posted so posts crossed. Your example is equally good.

gentlemen, thank you very much. My fingers are thick, but my phone is Galaxy with 8" display.

I am reading your detailed examples, all makes sense and I shouldn't have had trouble. I was doing pretty much all same, there is definitely an error on my side somewhere. Thank you again.  Cheers

Weiss

  • Full Member
  • ***
  • Posts: 209
Re: accessing array members in dictionary
« Reply #8 on: April 20, 2025, 02:01:53 am »
looks like I narrowed down the issue. This part does not work
Code: Pascal  [Select][+][-]
  1. high(SomeObjectDictionary[memberKey].someArray)
  2.  

as in

Code: Pascal  [Select][+][-]
  1.  
  2. for i:=low(SomeObjectDictionary[memberKey].someArray {this part works fine}) to high(SomeObjectDictionary[memberKey].someArray {this part does not work}) Do
  3. ...

I am getting compiler error, unrecognized symbol Lj548 - could be some other number with "Lj" prefix. 

I tried exploring a bit further, this function
Code: Pascal  [Select][+][-]
  1. arraySize:= length(someObjectDictionary[memberKey].someArray)
gives integer size outside of integer boundaries. I mean, integers that can be handled with my machine.

I am using Lazarus v2.2.6 on Windows 10 pro.

cdbc

  • Hero Member
  • *****
  • Posts: 2137
    • http://www.cdbc.dk
Re: accessing array members in dictionary
« Reply #9 on: April 20, 2025, 04:39:36 am »
Hi
GetArrayElement, SetArrayElement & CountArrayElements + property ArrayElements: Element-type.
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

TRon

  • Hero Member
  • *****
  • Posts: 4369
Re: accessing array members in dictionary
« Reply #10 on: April 20, 2025, 05:05:33 am »
looks like I narrowed down the issue. This part does not work
Les's try test that based on Thaddy's excellent example:
Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. {$mode objfpc}
  4. {$modeswitch arrayoperators}
  5. {$warn 6058 off} // inline not inlined
  6. {$warn 4046 off} // class with abstract method
  7. {$warn 5071 off} // private type not used
  8.  
  9. uses
  10.   types, sysutils, generics.collections;
  11.  
  12. type
  13.   TMyClass = class
  14.     public someArray: TIntegerDynArray;
  15.   end;
  16.  
  17.   TMyDictionary = specialize TObjectDictionary<string, TMyClass>;
  18.  
  19. var
  20.   MyDictionary : TMyDictionary;
  21.   MyClass      : TMyClass;
  22.   n, p         : sizeInt;
  23.   x, z, i      : sizeInt;
  24. begin
  25.   MyDictionary := TMyDictionary.Create;
  26.   try
  27.     // Fill dictionary with 5 items
  28.     x := 1;
  29.     for n := 1 to 5 do
  30.     begin
  31.       MyClass := TMyClass.Create;
  32.       // some random number of items to use for this Class' someArray
  33.       z := random(10);
  34.       for p := 0 to z do
  35.       begin
  36.         MyClass.somearray := MyClass.somearray + [x];
  37.         inc(x);
  38.       end;
  39.       // add MyClass to dictionary
  40.       MyDictionary.Add(n.ToString, MyClass);
  41.     end;
  42.  
  43.     // verify #1 -> display a single high
  44.     writeln('high(MyDictionary["3"].someArray) = ',
  45.              high(MyDictionary['3'].someArray)    );
  46.  
  47.     // verify #2 -> iterate from low to high
  48.     for n := 0 to MyDictionary.Count-1 do
  49.     begin
  50.       write('MyDictionary["', n+1, '"].somearray = [');
  51.       for i := low (MyDictionary[(n+1).ToString].someArray) to
  52.                high(MyDictionary[(n+1).ToString].someArray)
  53.       do write(' ', i);
  54.       write(']');
  55.       writeln;
  56.     end;
  57.  
  58.   finally
  59.     MyDictionary.Free;  // owns objects by default
  60.   end;
  61. end.
  62.  
  63. {
  64. output:
  65. ./test
  66. high(MyDictionary["3"].someArray) = 7
  67. MyDictionary["1"].somearray = [ 0 1 2 3 4 5]
  68. MyDictionary["2"].somearray = [ 0 1 2 3 4 5]
  69. MyDictionary["3"].somearray = [ 0 1 2 3 4 5 6 7]
  70. MyDictionary["4"].somearray = [ 0 1 2 3 4 5 6 7 8]
  71. MyDictionary["5"].somearray = [ 0 1 2 3 4 5 6]
  72. }
  73.  

Quote
... gives integer size outside of integer boundaries. I mean, integers that can be handled with my machine.
How many items are actually present in that array then ?

Quote
I am using Lazarus v2.2.6 on Windows 10 pro.
Unless you explicitly changed the compiler that also uses FPC 3.2.2 (output in example is using same compiler version but specifically for 64-bit).

PS: it start to look like it has nothing to do with the dictionary but rather that it is an issue related to the array (definition). Probably requires a bit more insight, or at least as much as you are willing/are able to share.

@cdbc: I've certainly used one too much to drink so what you wrote .... ?! uh !? did you mean to tell how fast you can hit a wall with an airplane or something ?  :)
« Last Edit: April 20, 2025, 05:21:46 am by TRon »
Today is tomorrow's yesterday.

Weiss

  • Full Member
  • ***
  • Posts: 209
Re: accessing array members in dictionary
« Reply #11 on: April 20, 2025, 05:31:48 am »
I don't know what to think. Created test bed here, and there are no issues
until I try accessing object through keys in dictionary:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Generics.Collections;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Button2: TButton;
  17.     Button3: TButton;
  18.     Memo1: TMemo;
  19.     Memo2: TMemo;
  20.     procedure Button1Click(Sender: TObject);
  21.     procedure Button2Click(Sender: TObject);
  22.     procedure Button3Click(Sender: TObject);
  23.     procedure FormCreate(Sender: TObject);
  24.   private
  25.  
  26.   public
  27.  
  28.   end;
  29.  
  30.   TClassWithArray = class(TObject)
  31.     iValue : integer;
  32.     arrayOfReal : array of real;
  33.   end;
  34.  
  35.   TListOfVariables = specialize TObjectDictionary<string, TClassWithArray>;
  36.  
  37. var
  38.   Form1: TForm1;
  39.   arrayClass : TClassWithArray;
  40.   listOfVariables : TListOfVariables;
  41.  
  42. implementation
  43.  
  44. {$R *.lfm}
  45.  
  46. { TForm1 }
  47.  
  48. procedure TForm1.FormCreate(Sender: TObject);
  49. begin
  50.     arrayClass:=TClassWithArray.create;
  51.     arrayClass.arrayOfReal:=[1,2,3,4];
  52.     listOfVariables:=TListOfVariables.create;
  53.     listOfVariables.add('key', arrayClass);
  54. end;
  55.  
  56. procedure TForm1.Button1Click(Sender: TObject);
  57. begin
  58.    memo1.Append(intToStr(length(arrayClass.arrayOfReal)));
  59. end;
  60.  
  61. procedure TForm1.Button2Click(Sender: TObject);
  62. begin
  63.   memo1.append(intToStr(length(listOfVariables['key'].arrayOfReal))) ;
  64. end;
  65.  
  66. procedure TForm1.Button3Click(Sender: TObject);
  67. var
  68.   i : integer;
  69.   output : string;
  70. begin
  71.    output:='';
  72.    for i:=low(arrayClass.arrayOfReal) to high(arrayClass.arrayOfReal) do
  73.    begin
  74.      arrayClass.arrayOfReal[i]:=i+5;
  75.      output:= output+' '+FloatToStrF(arrayClass.arrayOfReal[i],ffFixed,2,2);
  76.    end;
  77.    memo1.append(output);
  78.    //as soon as you un-comment the loop below, you get an error
  79.    {
  80.    for i:=low( listOfVariables['key'].arrayOfReal) to high(listOFVariables['key'].arrayOfReal) Do
  81.    begin
  82.  
  83.    end;
  84.     }
  85. end;
  86.  
  87. end.
  88.  


Weiss

  • Full Member
  • ***
  • Posts: 209
Re: accessing array members in dictionary
« Reply #12 on: April 20, 2025, 05:37:28 am »
however, when I work around by assigning array to an array outside of dictionary, then loop works fine

Code: Pascal  [Select][+][-]
  1. ...
  2. procedure TForm1.Button3Click(Sender: TObject);
  3. var
  4.   i : integer;
  5.   output : string;
  6.   arrayOfReal : array of real;
  7. begin
  8.    output:='';
  9.    for i:=low(arrayClass.arrayOfReal) to high(arrayClass.arrayOfReal) do
  10.    begin
  11.      arrayClass.arrayOfReal[i]:=i+5;
  12.      output:= output+' '+FloatToStrF(arrayClass.arrayOfReal[i],ffFixed,2,2);
  13.    end;
  14.    memo1.append(output);
  15.    arrayOfReal :=listOfVariables['key'].arrayOfReal;
  16.    for i:=low( listOfVariables['key'].arrayOfReal) to high(arrayOfReal) Do
  17.    begin
  18.  
  19.    end;
  20.  
  21. end;  
  22.  

TRon

  • Hero Member
  • *****
  • Posts: 4369
Re: accessing array members in dictionary
« Reply #13 on: April 20, 2025, 05:56:59 am »
I don't know what to think. Created test bed here, and there are no issues
until I try accessing object through keys in dictionary:
Thank you very much for the example. I'm a bit tired so probably overlook something but the good news is that I am able to reproduce (with pure fpc, no lcl).

(sorry named your example after your nick)
Code: Bash  [Select][+][-]
  1. linking weiss
  2. /usr/bin/ld: weiss.o: in function `P$WEISS_$$_CLICK3':
  3. weiss.pas:(.text.n_p$weiss_$$_click3+0x1e0): undefined reference to `.Lj2547'
  4. weiss.pas(79,1) Error: Error while linking
  5. weiss.pas(79,1) Fatal: There were 1 errors compiling module, stopping
  6. Fatal: Compilation aborted
  7. Error: ppcx64 returned an error exitcode
  8.  

fwiw: fixed in trunk (unable to test fixes right now)
« Last Edit: April 20, 2025, 05:59:38 am by TRon »
Today is tomorrow's yesterday.

Weiss

  • Full Member
  • ***
  • Posts: 209
Re: accessing array members in dictionary
« Reply #14 on: April 20, 2025, 06:23:34 am »
waitwaitwait. Function high() works fine. The issue is in for loop. If I assign high() to a variable and use it in the loop, loop works fine. But direct use of high() in for loop gives an error. For loop doesn't like high() function with array nested in object nested further in dictionary. Don't know if I make sense.

Have a look at another test

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button3Click(Sender: TObject);
  2. var
  3.   i : integer;
  4.   output : string;
  5.   arrayOfReal : array of real;
  6.   lastIndex   : integer;
  7. begin
  8.    output:='';
  9.    for i:=low(arrayClass.arrayOfReal) to high(arrayClass.arrayOfReal) do
  10.    begin
  11.      arrayClass.arrayOfReal[i]:=i+5;
  12.      output:= output+' '+FloatToStrF(arrayClass.arrayOfReal[i],ffFixed,2,2);
  13.    end;
  14.    memo1.append(output);
  15.  
  16.    lastIndex:=high(listOfVariables['key'].arrayOfReal);
  17.    for i:=low( listOfVariables['key'].arrayOfReal) to lastIndex Do
  18.    begin
  19.  
  20.    end;
  21.    memo1.Append('lenght of array = '+intToStr(length(listOfVariables['key'].arrayOfReal)));
  22.    memo1.Append(' last index = '+intToStr(lastIndex));
  23. end;        
  24.  

 

TinyPortal © 2005-2018