Auto Sorted Arrays (Source Included)

BlitzMax Forums/BlitzMax Programming/Auto Sorted Arrays (Source Included)

dw817(Posted 2015) [#1]
If you are still using string arrays to keep track of data, you might be interested in this little GEM that BlitzMAX provides its programmers, it is called a MAP type of variable.

The best way to describe it is to write code for it. It's a little tricky so I wrote two functions that should help you quite a bit:

strict
Global svar:tmap=New tmap,a$

varset "material","wood"
varset "fruit","apple"
varset "|alpha","10,10,20,20"

For a$=EachIn svar.keys()
  Print a$+"="+String(svar.valueforkey(a$))
Next
Print
Print varget$("fruit")
End

Function varget$(t$)
  Return String(svar.valueforkey(Lower$(t$)))
EndFunction

Function varset(a$,b$)
  a$=Lower$(a$)
  If b$=""
    MapRemove svar,a$
  Else
    MapInsert svar,a$,b$
  EndIf
EndFunction


You can see from varget$() and varset$() you have unique control over a data type that automatically sorts itself alphabetically each time you add to it.

The "|alpha" appears at the bottom because it's first character is higher ASCII then lowercase z. A method you can use to force items to the bottom of a list.

Now, the real trick is, how do I reverse it without using a FOR or REPEAT loop ? So I can have:

print revvarget("apple")

and the result would be "fruit" ?


Floyd(Posted 2015) [#2]
A map is like this: http://www.cplusplus.com/reference/map/map/

It stores key,value pairs. It doesn't work the other way around. If you were storing grades for students you might have

varset "Tom", "A"
varset "Dick", "B"
varset "Harry, "A"

You look up Tom and see that he got an A. You can't look up A and see who got that grade other than by examining everything and looking for A's.


Casaber(Posted 2015) [#3]
What if you sacrifice doubling the memory and keep the simplicty?
Like this. This makes me wonder though, isnīt keys meant to be unique always?
And the value.. not? So you have to be careful about uniqness in both.

I think itīs an interesting problem to solve elegantly and I think this might be a good start to double the memory and keeping things simple.

Strict
Global svar:tmap=New tmap,a$
Global svar2:tmap=New tmap

varset "material","wood"
varset "fruit","apple"
varset "|alpha","10,10,20,20"

For a$=EachIn svar.keys()
  Print a$+"="+String(svar.valueforkey(a$))
Next
For a$=EachIn svar2.keys()
  Print a$+"="+String(svar2.valueforkey(a$))
Next

Print
Print varget$("fruit")
Print vargetrev$("apple")
End

Function varget$(t$)
  Return String(svar.valueforkey(Lower$(t$)))
EndFunction

Function vargetrev$(t$)
  Return String(svar2.valueforkey(Lower$(t$)))
EndFunction

Function varset(a$,b$)
  a$=Lower$(a$)
  If b$=""
    MapRemove svar,a$ ; MapRemove svar2,a$
  Else
    MapInsert svar,a$,b$ ; MapInsert svar2,b$,a$
  EndIf
EndFunction



Brucey(Posted 2015) [#4]
That's not going to work :
MapInsert svar2,b$,a$


If you take Floyd's example, the second insert with key "A" will overwrite the first one with value "Tom".

You will need to store a list of entries in your map if you are intending on keying on a non-unique value and want to preserve all valid values for it.


Casaber(Posted 2015) [#5]
Ya uniquness would be needed everywhere. A and A would not work. Thatīs a good example when itīs not unqie everywhere.

The good part would be how to get over that, how DO bidirectional MAPs work?
I need to be in the right set of mindset to cope with this level of logic or I will explode.


Derron(Posted 2015) [#6]
If you have no 1:1 connection but a n:1 or 1:n you need to store them in another Map/array/list/...

So instead of having a map with "string-key":"string-value" you end up having "string-key":"map|list|array-value".

Each value then may contain 1-n objects.

When now reversing you would override the already existing key for each of the "1-n" (minus 1) objects.

It might be simpler, to store "key" next to the objects, so you are able to traverse through all values and checking their "key" to the one you are searching - you then are on your own to retrieve the first, last, best fitting.

So you end up with something like an "TMapEntry"-Wrapper:

Type TMapEntry
  Field obj:object
  Field key:string
End Type


Instead of looping over myMap.Keys() you then loop over myMap.Values()


For local o:object = EachIn myMap.Values()
  local entry:TMapEntry = TMapEntry(o)
  if not entry then continue
  
  'first find approach
  if entry.key.ToLower = searchKey then return entry 
Next


Obviously you could adjust the sample to filter for other criteria (image = searchImage, isAlive() = deadOrAlive ...)

This approach uses some casts and is less "lite" than the simple key-comparison.

If you know your keys, you could also insert some specifica of the object into the key - just construct it eg. this way:

key = typeName + "_" + objectID

so instead of storing everything of "bullets" as "bullet" you store "bullet_1", "bullet_2".
BUT you then are no longer able to do a simple "ValueForKey()" - as the key "bullet" is no longer existing.

I would not suggest above - but more likely the "key -> map/list/array-object". It all depends on what kind of types you want to store.


bye
Ron


dw817(Posted 2015) [#7]
Goodness. What a lot of ideas. :)

The one from Casaber is the one I had earlier in mind, at a sacrifice of coding and speed. To create =2= different SVARs so I can have a primary and secondary access. And yes, the results would be unique, there would be no item that matched either input or contents.

Not to give away too much of what I'm working on, the value is unique one, an identification mark, matching a unique set of text instructions (about 16-characters) and there is only one of each. There are no repeats.

Change the line:
MapRemove svar,a$ ; MapRemove svar2,a$
however, to:
MapRemove svar,a$ ; MapRemove svar2,b$

Very good idea though. There are going to be hundreds of values needed to be recalled at a moment's notice, so yeah, MapInsert svar2,b$,a$ is the quickest and easiest way to handle this little problem.