Storing lots of arrays inside a dictionary

I’m writing some kind of cryptography application.
I have a ComboBox and a TextField.
ComboBox contains every character in latin alphabets as its items.
The user can select the desired character, and write the replacement codes for that character in the TextField.
The user can assign multiple codes to each characters, and thus they must be stored in an array.

My question is, how can I create a dictionary that has arrays inside it for every character.
I want that dictionary to be accessible from other objects anywhere within the program.

[quote=163666:@Radium Radiovich]I’m writing some kind of cryptography application.
I have a ComboBox and a TextField.
ComboBox contains every character in latin alphabets as its items.
The user can select the desired character, and write the replacement codes for that character in the TextField.
The user can assign multiple codes to each characters, and thus they must be stored in an array.

My question is, how can I create a dictionary that has arrays inside it for every character.
I want that dictionary to be accessible from other objects anywhere within the program.[/quote]

Radium can you give an example of the sample data that you are needing to store?
Thanks

You can store arrays in a dictionary. Dictionaries store variants, so pretty much anything can go into a dictionary. When you lookup, you either need to cast the value, or predefine a variable or object of the right type before assigning the variant to it.

I would suggest encapsulating methods to store / extract data so you isolate your storage mechanism from the calling methods. You could use a module for that, call it “data”, create a private property “CodeDict” dictionary, make two protected methods: storeCodes and getCodes

By using a module, you can use the methods anywhere as “Data. storeCodes” and “Data.getCodes”. You can also change your storage methods anytime without affecting the rest of your code, as long as you keep both methods with the same parameters and return types.

Oh, and since dictionary values are case insensitive, and you may want to store different codes for “A” and “a”, you might want to convert “theChar” parameter to its ascii code.

Example (methods and property to be placed in a module)


sub StoreCodes(theChar as string,theCodes() as string)

  'make sure the dictionary is initialized - could be placed in an "init" method
  if CodeDict = nil then CodeDict = new dictionary

  'store
  CodeDict.value(asc(theChar)) = theCodes

end sub


sub getCodes(theChar as string) as string()

  'make sure the dictionary is initialized - could be placed in an "init" method
  if CodeDict = nil then CodeDict = new dictionary

  'find the char in the dictionary
  'decide what value to return if the code is not found.
  'Here we return an array with a single empty string element
  return CodeDict.lookup(asc(theChar),array(""))

end sub

[quote=163696:@Mike Cotrone]Radium can you give an example of the sample data that you are needing to store?
Thanks[/quote]
for example, the codes for “A” would be:
“sdfgfdgd, sadadad, sdasdaf, asdada, fgdfgd, weqfvcvx, ytygbcv”
which are cut into pieces with “,” as the delimiter, each piece is trimmed of any whitespace, and put inside an array with the rest of pieces.
Then upon encryption, every “A” in the text input is replaced randomly with one of the codes provided for “A”.

[quote=163726:@Bruno Frechette]You can store arrays in a dictionary. Dictionaries store variants, so pretty much anything can go into a dictionary. When you lookup, you either need to cast the value, or predefine a variable or object of the right type before assigning the variant to it.

I would suggest encapsulating methods to store / extract data so you isolate your storage mechanism from the calling methods. You could use a module for that, call it “data”, create a private property “CodeDict” dictionary, make two protected methods: storeCodes and getCodes

By using a module, you can use the methods anywhere as “Data. storeCodes” and “Data.getCodes”. You can also change your storage methods anytime without affecting the rest of your code, as long as you keep both methods with the same parameters and return types.

Oh, and since dictionary values are case insensitive, and you may want to store different codes for “A” and “a”, you might want to convert “theChar” parameter to its ascii code.

Example (methods and property to be placed in a module)

[code]

sub StoreCodes(theChar as string,theCodes() as string)

'make sure the dictionary is initialized - could be placed in an “init” method
if CodeDict = nil then CodeDict = new dictionary

'store
CodeDict.value(asc(theChar)) = theCodes

end sub

sub getCodes(theChar as string) as string()

'make sure the dictionary is initialized - could be placed in an “init” method
if CodeDict = nil then CodeDict = new dictionary

'find the char in the dictionary
'decide what value to return if the code is not found.
'Here we return an array with a single empty string element
return CodeDict.lookup(asc(theChar),array(""))

end sub

[/code][/quote]

Thank you!!!
two questions:

  1. How can I make two methods out of the code you wrote, without removing the sub header footer, and without filling the return type and input type inside Xojo’s GUI fields? I mean I want to do it all by code like you pros.
  2. I made two methods out of the code you provided (and I’m very thankful for that!), using the GUI, and I also made another method named NumToChar, which I explain it below my question.
    Then when I want to use them, I write “myModule.” and hit tab, StoreCodes, GetCodes, and CodeDict are all there in the autocomplete, but no trace of NumToChar is there.
    I tried putting it into another module, changed the scope, putted it under a custom control, none of them worked. it’s giving me a bad head ache. I think it’s a Xojo bug?
    Because when I create a new method under the module or custom control, it shows up as “Untitled” in the autocomplete, but just when I change its name, or paste my code inside it, it disappears from autocomplete.
    How can I solve this?

The NumToChar method explanation:
I have a ComboBox as I said, with items from A-Z, this method takes the ComboBox.listIndex and turns it into the corresponding character’s string.

Here is the code:

[code]sub NumToChar(n as integer) as string

select case n
case 0
return “A”
case 1
return “B”
case 2
return “C”
case 3
return “D”
case 4
return “E”
case 5
return “F”
case 6
return “G”
case 7
return “H”
case 8
return “I”
case 9
return “J”
case 10
return “K”
case 11
return “L”
case 12
return “M”
case 13
return “N”
case 14
return “O”
case 15
return “P”
case 16
return “Q”
case 17
return “R”
case 18
return “S”
case 19
return “T”
case 20
return “U”
case 21
return “V”
case 22
return “W”
case 23
return “X”
case 24
return “Y”
case 25
return “Z”

end sub[/code]

[quote=163740:@Radium Radiovich]for example, the codes for “A” would be:
“sdfgfdgd, sadadad, sdasdaf, asdada, fgdfgd, weqfvcvx, ytygbcv”[/quote]

You may want to create another dictionary with as keys the codes, so to decipher all you will have to do is to use the crypted string and it will fetch “A”.

Also, you can write your method as such :

sub NumToChar(n as integer) as string return chr(n+65) end sub

[quote=163760:@Michel Bujardet]You may want to create another dictionary with as keys the codes, so to decipher all you will have to do is to use the crypted string and it will fetch “A”.

Also, you can write your method as such :

sub NumToChar(n as integer) as string return chr(n+65) end sub[/quote]
Very neat! thanks a million!

How can I convert your code into a method, without using Xojo GUI? should I remove the sub header and footer, and instead enter it into the specific fields made for them inside the method editor GUI?

And I’m still having this problem with Xojo, some methods inside some modules/classes do not show up at all in autocomplete. no matter if they are shared or not, regular or protected…

You cannot create methods without the gui. The way we write the methods is just a standard way to write code. Here’s an example of how to fill the fields in Xojo. With this method:

sub getCodes(theChar as string) as string()

You would fill these fields:

  • Method name: getCodes
  • Parameters: theChar as string
  • Return type: string()

[quote=163740:@Radium Radiovich]Then when I want to use them, I write “myModule.” and hit tab, StoreCodes, GetCodes, and CodeDict are all there in the autocomplete, but no trace of NumToChar is there.
[/quote]

There’s no Xojo bug in the scope and autocomplete (nothing in the basic behaviour anyhow). What might be happening is you’re not handling the return value. For example, NumToChar returns a value: a string. Autocomplete will not work if you just type NumToChar.<tab> You have to handle its return value, ie: dim theResult as string = NumToChar.<tab> This is great, in fact, as it alerts you right as you type that something is wrong with your code since the method is supposed to return a value but you’re not handling it :slight_smile:

I agree with Michel. A dictionary is a better way to go. It would also allow to create several different dictionaries, or “encryptions” and switch from one to the other on the fly, maybe by setting a property in the module.

Aha, you are right!
Got it!
Thanks a lot.
Everyday I learn a (lot of) new thing(s) here :slight_smile:

I’m getting a KeyNotFoundException whenever I try to get the value of some key from CodeDict.
How can I solve this?

Here is my code so far,
My widgets:
charPicker = a PopupMenu
charCodeField = a TextField

==========

StoreCodes & GetCodes = same as what you posted

charCodeField [KeyDown Event]:

[code] dim chosenChar as string
dim codes() as string
chosenChar = myModule.NumToChar(charPicker.ListIndex)

if Keyboard.AsyncKeyDown(&h7E) then //UP key

codes = myModule.ProcessRawCodes(me.Text)
myModule.StoreCodes(chosenChar, codes)
myModule.MoveUp(charPicker, me.Text)

elseif keyboard.AsyncKeyDown(&h7D) then //DOWN key

codes = myModule.ProcessRawCodes(me.Text)
myModule.StoreCodes(chosenChar, codes)
myModule.MoveDown(charPicker, me.Text)

end if[/code]

myModule.MoveDown:

[code]
sub MoveDown(Picker as PopupMenu, text as string)

dim newChar as string

if Picker.listindex < Picker.listcount - 1 then
Picker.listIndex = Picker.listIndex + 1
newChar = NumToChar(picker.ListIndex)

if CodeDict.Value(newChar) = "" then
  text = ""
else
  text = CodeDict.Value(newChar)
end if

end if

end sub[/code]

myModule.MoveUp:

[code]
sub MoveUp(Picker as PopupMenu, text as string)

dim newChar as string

if Picker.listIndex > 0 then
Picker.listIndex = Picker.listIndex - 1
newChar = NumToChar(picker.ListIndex)

if CodeDict.Value(newChar) = "" then
  text = ""
else
  text = CodeDict.Value(newChar)
end if

end if
end sub[/code]

myModule.ProcessRawCodes:

[code]
sub ProcessRawCodes(CodesString as string) as string()

dim Untrimmed() as string
dim mightHaveSpaces() as string
dim Trimmed() as string

Untrimmed = Split(CodesString, “,”)

for index as Integer = 0 to Untrimmed.Ubound
mightHaveSpaces.Append(Untrimmed(index).Trim)
next

for index as Integer = 0 to mightHaveSpaces.Ubound
if mightHaveSpaces(index) <> “” then
Trimmed.Append(mightHaveSpaces(index))
end if
next

return trimmed
end sub[/code]

myModule.NumToChar:

[code]sub NumToChar(num as integer) as string

select case num
case 0
return “A”
case 1
return “B”
case 2
return “C”
case 3
return “D”
case 4
return “E”
case 5
return “F”
case 6
return “G”
case 7
return “H”
case 8
return “I”
case 9
return “J”
case 10
return “K”
case 11
return “L”
case 12
return “M”
case 13
return “N”
case 14
return “O”
case 15
return “P”
case 16
return “Q”
case 17
return “R”
case 18
return “S”
case 19
return “T”
case 20
return “U”
case 21
return “V”
case 22
return “W”
case 23
return “X”
case 24
return “Y”
case 25
return “Z”
case 26
return “0”
case 27
return “1”
case 28
return “2”
case 29
return “3”
case 30
return “4”
case 31
return “5”
case 32
return “6”
case 33
return “7”
case 34
return “8”
case 35
return “9”
case 36
return " "
end select

end sub[/code]

Use Lookup instead of Value.

Lookup produces the same error

Try replacing both places you have…

if CodeDict.Value(newChar) = "" then text = "" else text = CodeDict.Value(newChar) end if
with…

text = CodeDict.Lookup(newChar, "")

I’m guessing that’s where the KeyNotFoundException happens because newChar isn’t in there.

Okay, did that and I don’t get KeyNotFoundException anymore,
But when I press UP/DOWN arrowkeys, the text inside the TextField does not change at all.

I also tried this, with not success, I received two new errors.

[code] dim newChar as string
dim savedCode as string

if Picker.listindex < Picker.listcount - 1 then
Picker.listIndex = Picker.listIndex + 1
newChar = NumToChar(picker.ListIndex)

savedCode = CodeDict.Lookup(newChar, "")
if savedCode = "" then
  CodeDict.Constructor(newChar : "")
else
  if CodeDict.Value(newChar).ubound > 0 then
    savedCode = Join(CodeDict.Value(newChar, ", ")
  else
    savedCode = CodeDict.Value(newChar)
  end if
  
  text = savedCode
  
end if

end if[/code]

Hi Radium

I’m not sure I understand where you’re going with the code you provided. I’ll concentrate on the syntax and leave the logic to you. :slight_smile:

1.KeyNotFound exceptions means just that: the key you searched for in your dictionary was not found. As Tim and Will mentioned, use the lookup method instead so you can provide a default value to return if the key is not present in the dictionary. Or even better, use the GetCodes method of your module. It already uses the lookup method.

The idea behind having your dictionary as a private property in a module and creating methods to access and retrieve data from it is to isolate the storage method from the outside world. So you should not refer to your codeDict anywhere in your code, except in your module. To help you remember, make the codeDict property private. This way, it will not auto-complete outside the module and the compiler will give you an error if you still try to access it outside the module.

Also, remember that your dictionary stores arrays of strings, not indivual strings. At least, thats what your first question was. In the code below, your “text” variable is a single string, not an array, so you need to change it.

replace this code:

if CodeDict.Value(newChar) = "" then text = "" else text = CodeDict.Value(newChar) end if

with:

text=join(myModule.getCodes(newChar),",")

I would also suggest adding to your module a method to check if a specific char returns a code or not (if it exists). Like:

Sub CodeExistsForChar(theChar as string) as boolean

   'make sure the dictionary is initialized - could be placed in an "init" method
  if CodeDict = nil then CodeDict = new dictionary

   'return wether the dictionary has a key or not
  return CodeDict.HasKey(theChar)

end sub

Try:

Finally, some of the errors you should be able to find out for yourself, like the line 14 and 21 errors (in the last piece of code you tried) which are basic syntax errors: a missing parenthesis and unbalanced if-end if. And don’t forget, language reference is your friend.

oops, remove the “Try:” at the end of the code in my last post. Sorry.