Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

How make Associative array keys case sensitive


  • Please log in to reply
14 replies to this topic
dysmas
  • Members
  • 63 posts
  • Last active: Apr 06 2011 11:01 AM
  • Joined: 28 Dec 2010
I have used for years the scripts\dictionary_v2.ahk to provide arrays. It was working fine. Now that AHK has its own arrays, I tried to edit my scripts because I needed Unicode support.

Disappointment : Array keys are case insensitive. I looked in the FAQ, forum, Google and couldn't find an answer. Here is the test script.

StringCaseSense, On
    a1 := "ae"
    a2 := "AE"
    test22 := Object()
    test22[a1] := "Xxxxxx"
    test22[a2] := "Yyyyyyy"
    
    for index, element in test22 
        MsgBox % "Element number " . index . " is " . element

I would like it to return 2 elements, unfortunately there is only one.
I fear the arrays are really case insensitive (for keys I mean).
Am I wrong ?

Thanks if you have any information.

Dysmas

a4u
  • Guests
  • Last active:
  • Joined: --
May be of interest: Case sensitive variables possible?

a4u
  • Guests
  • Last active:
  • Joined: --
I suppose you could do something like this:
array := CSobj()
array[[color=#666666]"aB"[/color]] := [color=#666666]"item1"[/color]
array[[color=#666666]"AB"[/color]] := [color=#666666]"item2"[/color]
[color=#107095]MsgBox[/color], % array[[color=#666666]"aB"[/color]] [color=#666666]"`n"[/color] array[[color=#666666]"AB"[/color]]

[color=#107095]for[/color] k,v in array
	t .= k [color=#666666]" : "[/color] v [color=#666666]"`n"[/color]
[color=#107095]MsgBox[/color], %t%


CSobj() {
	[color=#107095]static[/color] base := [color=#107095]object[/color]([color=#666666]"_NewEnum"[/color],[color=#666666]"__NewEnum"[/color], [color=#666666]"Next"[/color],[color=#666666]"__Next"[/color], [color=#666666]"__Set"[/color],[color=#666666]"__Setter"[/color], [color=#666666]"__Get"[/color],[color=#666666]"__Getter"[/color])
	[color=#107095]return[/color], [color=#107095]object[/color]([color=#666666]"__sd_obj__"[/color], [color=#107095]ComObjCreate[/color]([color=#666666]"Scripting.Dictionary"[/color]), [color=#666666]"base"[/color], base)
}
	__Getter(self, key) {
		[color=#107095]return[/color], self.__sd_obj__.item(key)
	}
	__Setter(self, key, value) {
		self.__sd_obj__.item(key) := value
		[color=#107095]return[/color], false
	}
	__NewEnum(self) {
		[color=#107095]return[/color], self
	}
	__Next(self, ByRef key = [color=#666666]""[/color], ByRef val = [color=#666666]""[/color]) {
		[color=#107095]static[/color] Enum
		[color=#107095]if[/color] not Enum
			Enum := self.__sd_obj__._NewEnum
		[color=#107095]if[/color] Not Enum[key], val:=self[key]
			[color=#107095]return[/color], Enum:=false
		[color=#107095]return[/color], true
	}
NOTE - this is only set up to work for 1-dimensional arrays

dysmas
  • Members
  • 63 posts
  • Last active: Apr 06 2011 11:01 AM
  • Joined: 28 Dec 2010
Hello a4u,

thanks a lot for your quick answers ! I tried both versions of Scripting.Dictionary.
1) The simple one found in Case sensitive variables possible?, with the code :
sd := ComObjCreate("Scripting.Dictionary")
sd.item("aB") := "Name 1"
sd.item("AB") := "Name 3"
MsgBox, % sd.item("aB") "`n" sd.item("AB")

2) your code above.

Both solutions work, but your code is better for me because I had very little changes to do in my script since the syntax is similar to the syntax of Object(). I think this could be added to AutoHotkey documentation.
Everything is fine now. I would not have been able to find this myself.

I didn't explain what I was writing : I have used Allchars for years, but there is still no Unicode version in a previsible future. And since I couldn't wait forever, I wrote one myself with AHK, two years ago. But it was not possible (or I didn't found the way) to display Unicode in the Gui. Since the new Unicode version of AHK allows this easily, switching to it was a must. Since everything is running smoothly, when allchars-U will be in beta version, is it useful to post it somewhere ? Code has presently 300 lines, I expect between 500 and 1000 when it is finished.

thanks again, and thanks to all who worked on AHK

dysmas
  • Members
  • 63 posts
  • Last active: Apr 06 2011 11:01 AM
  • Joined: 28 Dec 2010
Hello a4u, I used extensively Scripting Dictionaries in the application I am working on (Unichars, now on SourceForge) with the code you provided above.

I added :
__Remover(self, key) {
       self.__sd_obj__.remove(key)
       return, true
Is it right ?

I found two problems that I am unable to solve.

When I call :
if !(myDic["mykey"])

if there is no key "mykey" in myDic, it is created with no value.

Probably related to the previous problem, I found that :
for key, val in myDic
does not work very well. The problem is mainly with numeric keys. Here is an example :
obj := CSObj()
;obj := Object()
obj["aA"] := "0xFF0000"
obj["02"] := "0x0000FF"
obj["00"] := "0x00FF00"



For k, v in obj
    s .= k "=" v "\n"
MsgBox % s

The result in the messagebox is :
aA=0xFF0000
02=
00=
=
2=
0=

I think I should use Enum, but I don't know how to use it. The following code gives nothing :
; Enumerate!
enum := obj._NewEnum()
While enum[k, v]
    t .= k "=" v "`n"
	obj.Next()
MsgBox % t


Could you give some help, please ?

a4u
  • Guests
  • Last active:
  • Joined: --

When I call :

[color=#107095]if[/color] !(myDic[[color=#666666]"mykey"[/color]])

if there is no key "mykey" in myDic, it is created with no value.

Please stick with "PG-13" or less rated object names. This issue is that the dictionary object creates the key is called.

The problem is mainly with numeric keys.

This is an issue with AHK converting everything to a string. On the AHK V2 roadmap:

• Change the way types are handled so that numbers can be distinguished from numeric strings. This also means that the format of a numeric literal such as 0x10 will be discarded and floating-point literals such as 1.0 will be pre-converted to binary (64-bit floating-point) format, rather than being stored as strings.

CSobj() {
[color=#107095]static[/color] base := [color=#107095]object[/color]([color=#666666]"_NewEnum"[/color],[color=#666666]"__NewEnum"[/color], [color=#666666]"Next"[/color],[color=#666666]"__Next"[/color], [color=#666666]"__Set"[/color],[color=#666666]"__Setter"[/color], [color=#666666]"__Get"[/color],[color=#666666]"__Getter"[/color])
[color=#107095]return[/color], [color=#107095]object[/color]([color=#666666]"__sd_obj__"[/color], [color=#107095]ComObjCreate[/color]([color=#666666]"Scripting.Dictionary"[/color]), [color=#666666]"base"[/color], base)
}
	__Getter(self, key) {
		[color=#107095]if[/color] self.__sd_obj__.exists(key)
			[color=#107095]return[/color], self.__sd_obj__.item(key)
	}
	__Setter(self, key, value) {
		self.__sd_obj__.item(key) := value
			[color=#107095]return[/color], false
	}
	__NewEnum(self) {
		[color=#107095]return[/color], self
	}
	__Next(self, ByRef key = [color=#666666]""[/color], ByRef val = [color=#666666]""[/color]) {
		[color=#107095]static[/color] Enum
		[color=#107095]if[/color] not Enum
			Enum := self.__sd_obj__._NewEnum
		[color=#107095]if[/color] Not Enum[key]
			[color=#107095]return[/color], Enum:=false
		[color=#107095]else[/color],
			val := self[IsStr(key)? key [color=#666666]""[/color]:key]	
		[color=#107095]return[/color], true
	}
IsStr(in) {
	Format := [color=brown]A_FormatInteger[/color]
	[color=#107095]SetFormat[/color], IntegerFast, Hex
	out := [color=#107095]SubStr[/color](in:=in, 1, 2)=[color=#666666]"0x"[/color]? False:True
	[color=#107095]SetFormat[/color], IntegerFast, %Format%
	[color=#107095]return[/color], out
}


sinkfaze
  • Moderators
  • 6367 posts
  • Last active: Nov 30 2018 08:50 PM
  • Joined: 18 Mar 2008

Please stick with "PG-13" or less rated object names...


My friend came over and asked me "Can I use your dictaphone?" and I said, "NO! You use your finger like everybody else!"

*ba-dum ching*

dysmas
  • Members
  • 63 posts
  • Last active: Apr 06 2011 11:01 AM
  • Joined: 28 Dec 2010
Please stick with "PG-13" or less rated object names. This issue is that the dictionary object creates the key is called.
I don't know what is PG-13. But I understand the problem : creates the key it called. So I searched a little more and it is clear that I must use the method exists.
The problem is that I can use the first syntax :
sd := ComObjCreate("Scripting.Dictionary")
sd.item("aB") := "Name 1"

if sd.exists("aB")
	msgbox  Found
This works. But all my program is written with the second syntax and I have been unable to integrate "exists" in the methods of CSObv() you indicated above. I would like something like that :

obj := CSObj()
obj["aA"] := "0xFF0000"
if obj.exists("aA")
	msgbox  Found
But it does not work.
The function I tried to define was :
__Exists(self, key) {
       return, self.__sd_obj__.Exists(key)       
   }

Implementing the "Keys" and "Items" methods in this syntax would be useful as well.
Otherwise I will have to make a serious rewrite of my script. :-(
Thanks if you have any idea.

a4u
  • Guests
  • Last active:
  • Joined: --

I don't know what is PG-13.

See PG -13 - though I think you entirely missed the tangent :roll:

Did you try the new code I posted?

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
which code? where is it?

dysmas
  • Members
  • 63 posts
  • Last active: Apr 06 2011 11:01 AM
  • Joined: 28 Dec 2010

I think you entirely missed the tangent

I fear you are right...

Did you try the new code I posted?

Yes I did, and it seems to work fine. Thank you.
Sorry if I didn't try at once, I had been trying to understand how I could use the exists function. I have a dirty workaround, but I am still interested in knowing how it could be done the right way. But I don't want to bother you, I have the essential now.

Thanks again.



a4u
  • Guests
  • Last active:
  • Joined: --

I had been trying to understand how I could use the exists function ... how it could be done the right way.

Well, I did use the exists method :wink: . It sounds like you are trying to make the AHK wrapper for the Scripting.Dictionary object function like the Scripting.Dictionary object itself. If that's the case, why use an AHK wrapper? Why not just use the Scripting.Dictionary object directly :p

dysmas
  • Members
  • 63 posts
  • Last active: Apr 06 2011 11:01 AM
  • Joined: 28 Dec 2010

If that's the case, why use an AHK wrapper? Why not just use the Scripting.Dictionary object directly

Well, the first reason was probably that you had written this to answer my question and I thought it was correct towards you to use your work. But the second reason was that I liked writing with this syntax array[key].
And once I had 800 lines written with plenty of arrays, I was too lazy to rewrite the whole thing (although it would have taken less time than I spent trying to understand this object oriented programming I am always lost in).
But now, with your modified version, everything is going fine. I no longer need the "keys" method, and I can use
if (array[key])
as an exist.

But thanks also to another thing : your idea of writing
var . ""
to force a variable to be a string was that type of simple idea which changes the life. I wouldn't have thought of it (perhaps PG-13 is a little high for me) and I used it heavily and it solved all problems with AHK mania to turn my strings in integers.

Everything working fine. Thanks a lot.

If anyone is interested to know how to use case sensitive arrays, see Unichars on Sourceforge, the AHK file is in the zip file.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

This is an issue with AHK converting everything to a string.

Not quite; it's a side-effect of the binary number caching system introduced in v1.0.48. When key is passed to the object in the expression self[key], the object converts its value, "01", to a binary integer and automatically "caches" this binary integer in key alongside its string value so that next time it won't need to do any conversion. (This has the same side-effect as the expression key + x.) Since 1 is not used as a key in the object, it then calls __Getter, copying both the string and the cached binary integer into its key parameter. Because key contains a binary integer, when __Getter passes key on to the dictionary object, it is passed as an integer.

Changing the expression self[key] to self[key ""] prevents it from affecting self's cache and forces only a string to be passed on to __Getter. Another solution would be to replace val:=self[key] with val:=self.__sd_obj__.item(key); this would also be more efficient.

dysmas
  • Members
  • 63 posts
  • Last active: Apr 06 2011 11:01 AM
  • Joined: 28 Dec 2010

Another solution would be to replace val:=self[key] with val:=self.__sd_obj__.item(key); this would also be more efficient.


Thanks, Lexikos, I noticed your remark that it would be more efficient. I changed my code and you are right, it is much faster.