Array of dictionaries

Hello guys, I’m new to xojo, but have some basic programming knowledge mainly from web languages.

I am trying to do the following: I want to read from the database and populate an array with the information. In the DB, there are items with 5 properties, like ID, parentID, name, etc. I read that there is no way to do an “Array of Arrays”, as I did before. After spending some time on google I found the suggestion of an array of dictionaries, which I then tried to follow. I’m not really sure, but I think it worked so far.
The Next step is to show these items from the array in a listbox. I figured the “for each” function would be the way to go. So good so far, this is the point were I keep getting errors and dont really know what to do… certainly a beginners mistake, but I cant get around it using google and the documentation. Can you help me maybe?

 static tagArray(-1) as dictionary //ID, parentID, name, desc, ordername, level
  
  dim dr as new DatabaseRecord
  dim sql as String
  
  sql = "select ID,parentID,name from table_tags"
  
  dim rs as RecordSet = db.SQLSelect(sql)
  
  while not rs.EOF
    
    dim d as new dictionary
    d.Value("ID") = rs.Field("ID").IntegerValue
    d.Value("parentID") = rs.Field("parentID").IntegerValue
    d.Value("name") = rs.Field("name").StringValue
    //d.Value("desc") = ""
    d.Value("ordername") = ""
    d.Value("level") = 0
    
    tagArray.Append(d)
    
    rs.MoveNext
  wend
  
  rs.close
  
  //Array anzeigen
  For each element as dictionary in tagArray
    Window1.lstTable.addRow(element.Value("name"))
  next

The error I get with this exact code is something like
“there are more than one elements with this name and it is not clear to which one this refers: Window1.lstTable.addRow(element.Value(“name”))”
with the words “Window1.lstTable.addRow” highlighted. when just putting a string inside of the addRow brackets, it works fine though.

I’m looking forward to your answers, thank you very much!

It looks great to me. You’re very close. Try this:

Window1.lstTable.addRow(element.Value("name").stringValue)

That seems to work. It’s a strange error though – I’m not sure why it needs it specified as a string.

Also, you might be better off using a .lookUp instead of .value – it’s a minor thing in this particular case, since you’re populating your dictionary in the code just above, but in your code, if a dictionary just happens to not have a “name” value it will crash your app. dictionary.Lookup prevents that by ensuring that a default value is passed if the item can’t be found:

Window1.lstTable.addRow(element.lookup("name", "Name not found").stringValue)

This will add a row called “Name not found” if a dictionary just happened to not have a “name” property.

It’s just a safer habit to get into when using dictionaries.

I think we need to know the exact error message. I have a feeling the problem is other than in this code, maybe in the controls you’ve added to the window?

A couple of things: For Each does not guarantee order of the array, so be sure that order doesn’t matter to you.

Since you’ve dimensioned the array with “static”, it’s going to keep getting larger every time this method is called, and it will always be the same array in every window that calls this method. Be sure that’s what you want.

Rather than a Dictionary, consider creating a custom class with the properties you need, then create an array of that class. You’ll never have to remember what key you used, it will be faster, and neater.

You can easily use array of arrays. What you can’t do is to get a value of an array in an array in one statement. There is no a(37)(2) in Xojo.

[code]static tagArray(-1) as dictionary //ID, parentID, name, desc, ordername, level

dim sql as String

sql = “select ID,parentID,name from table_tags”

dim rs as RecordSet = db.SQLSelect(sql)

while not rs.EOF

Dim a(5) As Variant 
a(0) = rs.Field("ID").IntegerValue
a(1) = rs.Field("parentID").IntegerValue
a(2) = rs.Field("name").StringValue
a(3) = ""
a(4) = ""
a(5) = 0

tagArray.Append(a)

rs.MoveNext

wend

rs.close

//Array anzeigen
For i As Integer = 0 To tagArray.UBound
Dim a() As Variant = tagArray(i) // <--------- !!!
Window1.lstTable.addRow(a(2))
next[/code]

No, Kem, something else is going on. I thought the same thing but did this quick test in Xojo (and yes, I’m not even bothering to initialize any of the dictionaries):

[code] dim a(100) as dictionary

for each d as dictionary in a
if d <> nil then
’ listBox1.addRow d.lookUp(“name”, “”)
listBox1.addRow d.lookUp(“name”, “”).stringValue
end
next[/code]

The first line (commented out) produces the error. The second works fine.

The error is the “There are several items with this name and it is not clear which one the call refers to” error (usually seen when you have two identically-named methods).

I have no idea why this happens. I’m curious.

The Dictionary Value() and Lookup() method return Variant. Since Xojo is a strongly typed language, you have to tell it what type is returned in this function.

Thank you all for your great answers! I will try a couple of these things.

I have a couple more questions now.

  1. If “for each” ignores the order, which function should I use to keep the order? Something with “for” and the length of the array, but I cant find an array.length or something like this.

  2. I used “static”, to keep this variable “alive”… What I wanted initially was to declare it as kind of a “global” variable which is accessible from all my scripts, but unfortunately I couldn figure out a way to do this yet. Any Suggestions?

I know, but shouldn’t the error message be different? Something like “type mismatch” instead of the message shown? Seems odd.

  1. You need Ubound which gives you the upper limit of an array:
for i as integer = 0 to arr.Ubound
e = arr( i )
  1. You can create a module, then add a property to it that is marked either Global or Protected. The latter would require the module name to be prefixed for access like MyModule.MyProp.

However, consider carefully if you really want to do this as it is often better to encapsulate properties within other classes or subclasses. For example, if you want multiple windows, perhaps each one needs its own array, in which case you’d make it a property of the window.

[quote=28085:@Simon Müller]1. If “for each” ignores the order, which function should I use to keep the order? Something with “for” and the length of the array, but I cant find an array.length or something like this.

  1. I used “static”, to keep this variable “alive”… What I wanted initially was to declare it as kind of a “global” variable which is accessible from all my scripts, but unfortunately I couldn figure out a way to do this yet. Any Suggestions?[/quote]

Use a for-next loop. You can find the the size of the array with the uBound() function. Just know what unbound is NOT the actual size – it’s the index of the last element. Since Xojo uses zero-based arrays, that actual count is one more. This works:

for i as integer = 0 to tagArray.ubound

As for a global array, just put your array property in a module and it is accessible throughout your application.