ID:163776
 
I'm looking for a small, reliable, and idiot-proof way of determining whether a list uses associative indeces (like list1["blah"]="blahblahblah") or numeric (a regular list2[1]="blahblahblah" list).

What I've tried was:
proc/TMDI_isAssociativeList(var/list/l)
if( (!l) || (!(istype(l,/list))) ) return FALSE

for(var/v in l)
if(l[v]) return TRUE
return FALSE


Now, that works ok, excepting the case where you've got simple numeric values as the list data. IOW, a regular numerically indexed string will return TRUE if the list data at l[x] is 1, 2, 3 or any numeric index within l.len. A list entry like l[x] = 0 will return a list index out of bounds error.

So, I added a line:
proc/TMDI_isAssociativeList(var/list/l)
if( (!l) || (!(istype(l,/list))) ) return FALSE

for(var/v in l)
if(isnum(v)) return FALSE
if(l[v]) return TRUE
return FALSE


Which will return an incorrect FALSE if the associative list is indexed with numbers. It also seems to me this will break in some other circumstance, but I can't put my finger on it.

Is there a way to do this well?
What am I missing?
As far as I know an "associative list indexed with numbers" is just the same as a regular non-associative list, only you're assigning items to specific positions within the list, and any other list positions that exist but weren't given a value would be null.

So given that, I suppose you could just check to see if there's any values in the list that are are null.

for(var/v in l)
if(!v) return FALSE


If that doesn't work, then:
for(var/n in 1 to l.len)
if(!l[n]) return FALSE
This method should always work, although I'm unsure of its speed and it's somewhat hacky:

proc/TMDI_isAssociativeList(var/list/l)
if( (!l) || (!(istype(l,/list))) ) return FALSE
return findtext(list2params(l),"=")


In response to DarkCampainger (#2)
Ok, so I found a couple of things after looking over y'alls code and mine and doing a little more exploring.

1) You can't create an associative list with numbers as the indeces (at least not one where the numbers skip in sequence). IOW, this code:
var/list/xlist = list(1="text1",3="text3")

will generate an out of bounds runtime. As will:
var/list/xlist = list()
xlist["alpha"] = "textAlpha"
xlist[1] = "text1"
xlist[3] = "text3"

Apparently, even a list that's previously set to be an associative list will give a runtime if you attempt to use a numeric index that skips in sequence.

2) Associative lists are (in some ways) paired lists. You can successfully cycle through both sets of info using the same list:
var/list/xlist = list("name1"="text1","name2"="text2")
for(var/v in xlist)
world << "v = [v]" //this will output "name1" and "name2"
//AND!
for(var/n in 1 to l.len)
world << "xlist\[n\] = [(xlist[n])]" //this will output "name1" and "name2" as well

That surprised me. In other words, xlist[1] = "name1" and xlist["name1"] = "text1". Most everyone probably knew that, but I didn't.

3) Given xlist above it would seem we could (maybe) reset the name (or index) of the first element without changing it by saying xlist[1] = "name3". I assumed this might make xlist["name3"] = "text1", but it doesn't. It makes xlist[1] = "name3", xlist["name3"] = null, AND xlist["name1"] = null. In other words, as far as I can tell, it removes both the data from that first element entirely. Other associative entries (like xlist["name2"]) remain unchanged.

Foomer wrote:
If that doesn't work, then:
for(var/n in 1 to l.len)
> if(!l[n]) return FALSE
I thought this might work, too, until I discovered 2 above. l[n] will always return TRUE for an any associative list here.

DarkCampainger wrote:
> proc/TMDI_isAssociativeList(var/list/l)
> if( (!l) || (!(istype(l,/list))) ) return FALSE
> return findtext(list2params(l),"=")
>

It may be hacky but it does work reliably (as far as I can tell). I'll use it as a way of deciding rare ambiguous cases .