Associative Array Errors - n00b

I am trying to create an associative array of coordinates, something like { {x:40,y:50}, {x:40,y:51}} , etc.


a_poster_pos = CreateObject("roAssociativeArray")

' l_x and l_y are incremented in a loop
position = {}
position["x"] = l_x;
position["y"] = l_y;
   
a_poster_pos.Append(position);

Since I am new to this, it of course doesn’t work and I get “Syntax Error” compilation errors

Don’t end a line with a semicolon in brightscript.

I can’t tell from your description but I suspect what you actually want is an array of AAs. You want an arbitrary number of AAs in your top level container, right? Make it an array and Push each AA onto it.

–Mark

I wouldn’t even do AA, just make it an array of arrays

a_poster_pos = []

' l_x and l_y are incremented in a loop
position = []
position.Push(l_x)
position.Push(l_y)
   
a_poster_pos.Push(position)

later you know that each array has two values, in slots 0 and 1, which you can check
a_poster_pos[0][0] would give you the x value for position 1
a_poster_pos[12][1] would give you the y value for position 13

If I do it this way:


a_poster_pos = []

' l_x and l_y are incremented in a loop
position = []
position.Push(l_x)
position.Push(l_y)
   
a_poster_pos.Push(position)

How can I get the x and y coordinates iterating through a for loop ?


for each coordinate in a_poster_pos
		print "x : ";coordinate[].x
		print "y : ";coordinate[].y
end for

I think this is what you’re looking for:


a_poster_pos = []

' l_x and l_y are incremented in a loop
a_poster_pos.Push({x: l_x, y: l_y})


for each coordinate in a_poster_pos
		print "x : ";coordinate.x
		print "y : ";coordinate.y
end for

EDIT: You can use arrays instead of AAs, as Komag suggested, although I wouldn’t recommend it unless you have some extremely compelling performance reason for using a numeric-based array index. It’s much better programming practice to call the y-coordinate “y” and the x-coordinate “x”, rather than 0 and 1 (or was it 1 and 0 – see what I mean?). That might work for baby programs, but can really mess you up as programs become larger and more complex.

for each coordinate in a_poster_pos
      print "x : ";coordinate[0]
      print "y : ";coordinate[1]
end for

No need to use an AA, just adds what appears to be unnecessary complexity, unless it’s more clear to you that way to have the x and y labels written there every time, then go ahead

I get a type mismatch with that for loop at this line :


Type Mismatch. (runtime error &h18) in pkg:/source/main.brs(95)
095:       print "x : ";coordinate[0]

“coredump” wrote:
I get a type mismatch with that for loop at this line :


Type Mismatch. (runtime error &h18) in pkg:/source/main.brs(95)
095:       print "x : ";coordinate[0]

I think it’s telling you that ‘coordinate’ is not an array. Check that it’s been set up correctly. Before this statement, add:

print coordinate
print type (coordinate)

to see if it actually prints out some array items.

Although you might want to go back and read my previous post, including the edit I added after posting.

“Komag” wrote:

for each coordinate in a_poster_pos
      print "x : ";coordinate[0]
      print "y : ";coordinate[1]
end for

No need to use an AA, just adds what appears to be unnecessary complexity, unless it’s more clear to you that way to have the x and y labels written there every time, then go ahead
I’m with belltown on this one. Using an AA allows you to easily keep track of what you’re storing, and allows you to easily add new fields should you decide you need to in the future (e.g., a corresponding width and height), without having to worry about remembering what each index in the array is storing. An array of arrays is much more complex to manage, and a lot harder to decipher should you need to come back to the code later, in my opinion.

That makes sense, clarity with labeling

Explicit types are good… I’d recommend something like this:


function pointCreate(x as Integer, y as Integer) as Object
   return {
      x: x,
      y: y,

      toStr: function() as String
         return "point("+str(m.x)+", "+str(m.y)+")"
      end function
   }
end function

function main() as Void
   points = [] ' Create a new array

   ' Create points, and add them to list   
   for x = 0 to 9
      point = pointCreate(x, 10) ' All points have y-coordinate of 10
      points.push(point)
   end for

   ' Output points
   print "The Points are: "
   for each point in points
      print point.toStr()
   end for

  ' Example accessing single x, y
  point = points[0] ' Get first Point
  print "First Point's X: ";point.x
end function

@TheEndless , I would just note that if you are trying to squeeze the maximum performance from your code, for example in a game, it is best to avoid using Associative Arrays if possible - array access for Associative Arrays is still significantly slower than accessing a linear array, even though we’ve did some of work to significantly improve AA performance.

  • Joel

“RokuJoel” wrote:
@theEndless, I would just note that if you are trying to squeeze the maximum performance from your code, for example in a game, it is best to avoid using Associative Arrays if possible - array access for Associative Arrays is still significantly slower than accessing a linear array, even though we’ve did some of work to significantly improve AA performance.

  • Joel
    Thanks for the note, but if accessing an AA is slow enough to make an appreciable difference in framerate, then there are some serious issues there…

Just to be sure, I just did the following test, which gave me the exact opposite results of what you are suggesting:

    a = [0, 0]
    aa = { x: 0, y: 0 }
    timer = CreateObject("roTimespan")
    
    for loop = 1 To 10
        timer.Mark()
        for i = 1 to 100000
            test = a[0]
        next
        ?"A Loop:";timer.TotalMilliseconds()
        timer.Mark()
        for i = 1 to 100000
            test = aa.x
        next
        ?"AA Loop:";timer.TotalMilliseconds()
    next

Output:

------ Running ------
A Loop: 179
AA Loop: 75
A Loop: 145
AA Loop: 70
A Loop: 77
AA Loop: 68
A Loop: 78
AA Loop: 73
A Loop: 78
AA Loop: 67
A Loop: 78
AA Loop: 69
A Loop: 78
AA Loop: 67
A Loop: 78
AA Loop: 69
A Loop: 76
AA Loop: 69
A Loop: 77
AA Loop: 70

In every loop, accessing the AA was ~10% faster.

That aside, note also that that’s looping over the AA 100,000 times, which is nowhere near what you’d realistically need to do in a single frame of animation. Reduce that to 1000 loops, and the processing time is almost immeasurable.

EDIT: I just ran the same test on the Roku 2 and got slightly different results, but still nothing significant enough to be concerned about it affecting framerate, IMO.

------ Running ------
A Loop: 211
AA Loop: 149
A Loop: 140
AA Loop: 148
A Loop: 150
AA Loop: 149
A Loop: 135
AA Loop: 154
A Loop: 138
AA Loop: 154
A Loop: 138
AA Loop: 151
A Loop: 136
AA Loop: 147
A Loop: 135
AA Loop: 151
A Loop: 139
AA Loop: 147
A Loop: 137
AA Loop: 148

“TheEndless” wrote:

“RokuJoel” wrote:
@theEndless, I would just note that if you are trying to squeeze the maximum performance from your code, for example in a game, it is best to avoid using Associative Arrays if possible - array access for Associative Arrays is still significantly slower than accessing a linear array, even though we’ve did some of work to significantly improve AA performance.

  • Joel
    Thanks for the note, but if accessing an AA is slow enough to make an appreciable difference in framerate, then there are some serious issues there…

Just to be sure, I just did the following test, which gave me the exact opposite results of what you are suggesting:

Great minds think alike. I just finished doing the same thing with similar results:


function main() as Void
   timer = createObject("roTimespan")
   accessCount = 0

   ' Setup map containing alphabet
   map = {}
   for i = asc("a") to asc("z") - 1
      map[chr(i)] = i
   end for

   ' Create array containing only keys to the map.
   keys = []
   for each key in map
      keys.push(key)
   end for

   ' Time the whole loop without the AA lookups. This will be used as the
   ' 'maintenanceTime' (i.e. time to perform loop iterations and increment
   ' counter)
   timer.mark()
   for i = 0 to 999999
      for each key in keys
         accessCount = accessCount + 1
      end for
   end for
   maintenanceTime = timer.totalMilliseconds()

   ' Perform 1 million lookups on each element in AA.
   accessCount = 0
   timer.mark()
   for i = 0 to 999999
      for each key in keys
         elem = map[key]
         accessCount = accessCount + 1
      end for
   end for

   ' Calculate total elapsed time.
   elapsed = timer.totalMilliseconds() - maintenanceTime

   ' Results
   ?"Iterations    : ";accessCount
   ?"Total Ellapsed: ";elapsed;" ms"
   ?"Average Time  : ";(elapsed / accessCount)
end function

And here are my results on Roku 3:

Iterations : 25000000
Total Ellapsed: 19830 ms
Average Time : 0.0007932

It looks like it takes ~0.0008 ms to perform AA lookup.

Maybe very short keys help it be faster? Same results with keys that are many character’s long?

“Komag” wrote:
Maybe very short keys help it be faster? Same results with keys that are many character’s long?
Increasing the length of the key does seem make a difference, but still not significant enough, in my opinion, to warrant abandoning the use of AAs when working with the 2D API.

Now, changing from the shorthand dot notation to brackets makes a huge difference (e.g., aa.key vs aa[“key”]), nearly doubling the processing time, so I’d avoid using key names that require brackets whenever possible.

sometimes, it is really great to be wrong. Thanks for the benchmarks!

  • Joel

“RokuJoel” wrote:
sometimes, it is really great to be wrong. Thanks for the benchmarks!

  • Joel
    Hey Joel, any idea why aa[“key”] is so much slower than aa.key? I’d have expected the opposite to be true.

With aa.key the hash of the key name can be computed at compile time. With aa[var], it needs to be done at runtime. When var is a literal string it’s possible it could also be done at compile time but I think the compiler treats a string as a var in this case and defers it to runtime.

–Mark

Associative Arrays also offer a limited type of object containment which is a natural fit for BS, ( consider GetGlobalAA ). They provide a means to contain data and methods which act upon that data using the m pointer. For code intensive applications such as the 2D API, Associative Arrays are a very efficient way to build complex frameworks. While there is no Base Class inheritance per-say, Function pointers can be overridden to act upon the base data in extended classes. The extended class would create an instance of the base class and provide another method for the base function pointers. These overrides should be assigned in the new objects initialization function and not in the new objects declaration. This also makes it easy to adapt to different ROKU environments as dictated by device objects. Serious programmers would do well to learn how to use them beyond a “dictionary”. While the m pointer is limited to the contents of the AA, member references of the new AA can be set to global Objects during declaration and then used naturally.