How do you attempt to find a key in a dictionary, without throwing an error (or doing anything else) if the key isn’t found?
hmm over here I’m seeing Dictionaries return nil when I query a key that doesn’t exist… otherwise you can always try try
or protect
if you’re doing something more involved…
That’s odd… I wasn’t getting that here. Must have been an error somewhere else in my code, I guess.
This is one reason why it’s helpful to provide some details on the error – one rule of thumb for a tech forum is, if you catch yourself writing “an error,” it’s probably a good idea to copy/paste error text and maybe code snippets too.
At present on this question, we’re guessing.
That’s normal behavior for SC collections: key or index doesn’t exist → nil result. (SC doesn’t even have an array out of bounds error: if a = [1, 2, 3]
, then a[100]
and even a[-1]
just return nil, where other languages would throw an error.)
Does your code handle a nil result from the dictionary lookup?
hjh
Good points. I was working on the code at work, and thought it had synced to the cloud, but it appears not, so don’t have it at home.
I’m pretty sure I was testing for a ‘nil’ return, though, and was getting a string of errors in the Post Window.
I’m just starting out in SCLang, so it was probably a syntax error somewhere else.
I was trying to do this, I think:
~ccMapping = (
"74": (\desc: "Envelope Attack", \busIndex: 0),
"71": (\desc: "Envelope Decay", \busIndex: 1)
);
MIDIIn.connectAll;
MIDIdef.cc(\someCC, {
arg a, b;
[a, b].postln;
if(~ccMapping[b.asString] != nil, {
postln(~ccMapping[b.asString][\desc] + ": " + a);
});
});
I’m not getting any errors, now, but I’m also not managing to select either item in the dict.
I know I can do this with an array for every possible CC number, selecting an array index directly with the CC number, but that seems pretty inefficient, if I’m only assigning a small number of CCs.
You may not use strings as keys in an Event (or IdentityDictionary). Use \symbols
instead. You can convert strings to symbols by .asSymbol
.
EDIT: Integer CC numbers are also ok as keys: myDict.put(74, ...)
. Not sure that’s valid for (key: value)
syntax though.
hjh
This seems to work!
s.boot;
// Connect all available MIDI interfaces
MIDIIn.connectAll;
(
~paramCCs = (
73: (\desc: "Envelope Attack", \busIndex: 0),
75: (\desc: "Envelope Decay", \busIndex: 1)
);
// Create k-rate bus
~midiBus = Bus.control(s, 32);
// Match cc 127, all channels
MIDIdef.cc(\test3, {
arg val, cc, ch;
if(~paramCCs[cc] !== nil, {
// Set value in 0 - 1 range to bus corresponding to CC number
~midiBus.setAt(~paramCCs[cc][\busIndex], val.asFloat/127);
// Post value + description string
postln("CC: " + cc + " " + ~paramCCs[cc][\desc] + ": " + val);
});
}, (1..127), 0);
)
For some reason, I was assuming keys had to be strings, and couldn’t start with a number.
Seem they don’t, and they can…
The catch is this:
(
a = "abc";
b = "abc";
[a == b, a === b]
)
-> [ true, false ]
Strings may be equivalent (==
meaning they have the same contents) but not identical (===
meaning they are the same physical object).
Dictionary
looks up keys by ==
– so it supports any object type as a key, but it’s slower.
IdentityDictionary
looks up by ===
(“identity”) – so the keys should be members of an atomic class: Symbol, Integer, Char, Boolean. (Not Float because floating-point equality is not always reliable: (0.1 ! 10).sum == 1.0
is false.)
(
x = Dictionary[
"xyz" -> 0,
\abc -> 1
];
y = IdentityDictionary.newFrom(x);
[x["xyz"], x[\abc], y["xyz"], y[\abc]]
)
// "xyz" match for Dictionary, but not IdentityDictionary
-> [ 0, 1, nil, 1 ]
Event follows IdentityDictionary here.
hjh
Hmm… so I should use IdentityDictionaries, rather than Dictionaries, to speed up retrieval of values?
You are already using IdentityDictionary.
Event : Environment...
Environment : IdentityDictionary...
Event is a type of IdentityDictionary. So you don’t have to do anything different.
hjh