Look Up Key In Dictionary With Silent Fail

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

1 Like

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