Dictionaries#
The .dict family: keyed, nested data.
13 objects. Every entry is
pulled directly from the engine registry, which reads the ADD_DESCRIPTION
/ INLET_DOC / OUTLET_DOC / PARAM_DOC macros in the libYSE
source; the longer notes under each summary are ported from the libYSE
documentation. The ~ prefix marks DSP / audio-rate objects; . marks
control-rate objects. Links into the patcher guide (messages, time, data,
files, …) lead to the libYSE documentation, which also has this
category’s page.
.dict#
A key/value store shared by name. Keys nest with ‘::’ (voice::1::freq), a value is any message, and the contents are saved with the patch. A bang sends the reference ‘dictionary <name>’ that the other .dict objects take.
- Dart:
Obj.gDict- Kind:
Control-rate
A dictionary holds entries: a key path and a value. Keys nest with ::,
so voice::1::freq is one entry three levels deep. A value is the text of
any message, so set chord 0 4 7 stores one value of three numbers. See
Nested keys in Collections, dictionaries and arrays for how the nesting works.
Messages
set <path> <value...>: store a value, replacing the one already there.set <path>with no value stores an empty value.get <path>: send the value out of outlet 0. One number leaves as an int or a float, anything else as a list. When the path is missing, outlet 2 bangs instead. A path holding an empty value sends nothing at all.delete <path>: remove one entry. A sub-tree is not an entry:delete voicedoes not removevoice::1::freq(use.dict.strip).clear: remove every entry.getsize: send the number of entries out of outlet 0.getkeys: send the top-level keys out of outlet 0, each once, in the order they were first written.bang: send the reference
dictionary <name>out of outlet 1.
Sharing
Every .dict with the same name in a patcher uses the same dictionary, and
two patchers with the same name share theirs. A .dict without a name
keeps a private dictionary and sends no reference. The contents are saved
with the patch as nested JSON. See Sharing a store by name and
What is saved with the patch.
The other .dict objects
The other objects in this category each work on one or more named dictionaries. They take the names as creation arguments, and the names cannot be changed while the patch runs. A cord cannot carry a dictionary, so an object that produces one writes it into a dictionary you name, rather than sending it (see Dictionaries and arrays travel by name). They share these rules:
A bang on the left inlet runs the object. So does the reference
dictionary <name>of the dictionary it reads, so outlet 1 of a.dictcan be wired straight into it. A reference that names any other dictionary is refused.Any extra inlet accepts the reference of the dictionary it stands for and does nothing with it, so the wiring can show which dictionaries an object uses.
An object that writes a dictionary replaces its whole contents (
.dict.stripedits it in place), then sends that dictionary’s reference. So the objects chain: the reference that leaves one can run the next.Each run works on the dictionary as it was when the run started. Changes made while it sends, even by objects it is wired to, show up only on the next run.
An object without a name works on a private, empty dictionary and sends no reference.
set voice::1::freq 440 -> [.dict synth]
bang -> [.dict synth] outlet 1: dictionary synth
dictionary synth -> [.dict.serialize synth] -> {"voice":{"1":{"freq":440}}}
Limits
A dictionary holds at most 256 entries. A key path is at most 128 characters,
including the :: separators, and a value at most 256. A message that
does not fit is refused: it changes nothing, sends nothing and is not
logged. The same refusal happens when two threads use one dictionary at the
same moment: the second one does nothing rather than wait. See
Size limits.
Safe on the audio thread: no message allocates memory, takes a lock or
waits. Only .dict.deserialize does part of its work elsewhere.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
in |
BANG, LIST |
at most 256 entries |
‘set <path> <value…>’ stores a value, ‘get <path>’ sends one, ‘delete <path>’ removes one, ‘clear’ empties the dictionary, ‘getsize’ sends the entry count and ‘getkeys’ the top-level keys. A bang sends the reference out of outlet 1. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
data |
ANY |
— |
The value a ‘get’ fetched: one number leaves as an int or a float, anything else as a list. Also the count from ‘getsize’ and the key list from ‘getkeys’. |
1 |
reference |
LIST |
— |
‘dictionary <name>’ on a bang: the reference the other .dict objects accept. Silent for an unnamed dictionary. |
2 |
miss |
BANG |
— |
Bang when a ‘get’ names a path the dictionary does not hold. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
name |
— |
any identifier |
The dictionary’s name. Every .dict with this name in the patcher shares one dictionary. Empty gives this object a private dictionary. Cannot be changed while the patch runs. |
.dict.compare#
Compares two named dictionaries and sends 1 when they hold the same entries, 0 when they do not. On a difference, the key paths that differ follow from the right outlet.
- Dart:
Obj.gDictCompare- Kind:
Control-rate
Two dictionaries are equal when they hold the same key paths with the same
values, in any order. Values are compared as text, so 7 and 7.
differ.
On a difference, the paths that differ follow the verdict: first the paths that are only in the left dictionary or hold a different value, in the left dictionary’s order, then the paths that are only in the right one. The verdict is sent first, so a patch can act on it before the list arrives.
Both arguments may name the same dictionary. At most 256 paths are sent.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
compare |
BANG, LIST |
— |
A bang compares the two dictionaries. So does the left dictionary’s reference ‘dictionary <name>’. |
1 |
right reference |
LIST |
— |
Accepts the right dictionary’s reference and does nothing with it, so its .dict can be wired here. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
equal |
INT |
0 or 1 |
1 when the two dictionaries hold the same entries, 0 when they do not. Sent before the differing paths. |
1 |
paths |
ANY |
at most 256 paths |
The key paths that differ, as a list, sent after the verdict. Silent when the dictionaries are equal. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
left |
— |
any identifier |
The name of the left dictionary. Empty compares an empty dictionary. |
right |
— |
any identifier |
The name of the right dictionary. Empty compares an empty dictionary. |
.dict.deserialize#
Fills a named dictionary from JSON text, replacing its contents, and sends its reference. Takes a JSON object as a message, or ‘read <file>’ for a larger document.
- Dart:
Obj.gDictDeserialize- Kind:
Control-rate
The message must be one JSON object, such as the text .dict.serialize
sends. It replaces the contents of the named dictionary, and the reference
then leaves the outlet:
Nested objects become
::key paths.An array becomes a list value. Arrays and objects inside an array are dropped.
trueandfalsebecome1and0;nullbecomes an empty value.
The JSON is read in the background, so the dictionary changes and the reference is sent one audio block after the message arrives, not at once. A document that is not valid JSON, or is not an object, changes nothing and sends nothing. A document that arrives while the previous one is still being read is refused.
Files
A message holds at most 255 characters. For a larger document use read
<file>, which loads a JSON file of up to 128 KiB and then works like a
message. A bare read reads the last file again. See File input and output.
Entries past 256, or with a key path or value that is too long, are dropped and the rest of the document still loads.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
json |
LIST |
at most 255 characters inline; a file up to 128 KiB |
A JSON object as one list message, such as the text .dict.serialize sends. It replaces the dictionary’s contents one block later. ‘read <file>’ loads the JSON from a file; a bare ‘read’ reads the last file again. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
reference |
LIST |
— |
‘dictionary <name>’ once a document has been loaded. Silent for an unnamed dictionary and for a document that could not be read. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
name |
— |
any identifier |
The name of the dictionary to fill. Empty fills a private dictionary and sends nothing. |
.dict.group#
Groups a named dictionary’s entries by value into a target dictionary, as ‘<group>::<original path>’. With a key argument, each record is grouped by the value it holds at that key.
- Dart:
Obj.gDictGroup- Kind:
Control-rate
Each entry of the source is written into the target as
<group>::<original path>, with its value unchanged. Without a key
argument, an entry’s group is its own value, which turns a table into an
index. With a key argument, the source is read as records: the first part of
each path names a record, and every entry of the record groups under the
value the record holds at that key.
source notes: n1::inst bass n1::pitch 40 n2::inst lead n2::pitch 72
[.dict.group notes byinst inst]
target byinst: bass::n1::inst bass bass::n1::pitch 40
lead::n2::inst lead lead::n2::pitch 72
An entry is skipped when its group is empty, when its record has no entry at the key, or when the new path would be longer than 128 characters.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
group |
BANG, LIST |
— |
A bang groups the source dictionary into the target and sends the target’s reference. So does the source dictionary’s reference ‘dictionary <name>’. |
1 |
target reference |
LIST |
— |
Accepts the target dictionary’s reference and does nothing with it, so its .dict can be wired here. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
reference |
LIST |
— |
‘dictionary <target>’ after each grouping. Silent for an unnamed target. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
source |
— |
any identifier |
The name of the dictionary to group. Empty groups an empty dictionary. |
target |
— |
any identifier |
The name of the dictionary that receives the result. Each grouping replaces its contents. |
key |
— |
any key path segment |
The key each record’s group is read from: with ‘instrument’, the entries under ‘<record>::’ group under the value at ‘<record>::instrument’. Empty groups each entry by its own value. |
.dict.iter#
Walks a named dictionary, sending one ‘<path> <value>’ list per entry, then a bang from the right outlet when it is done.
- Dart:
Obj.gDictIter- Kind:
Control-rate
Each entry leaves as one list: its whole :: path, then its value. The
entries leave in the order they were first written. When the last one has
been sent, the right outlet bangs, also for an empty dictionary.
Each pair is handled completely, including everything it causes downstream, before the next pair leaves. Use the done bang for whatever should happen after the walk. A run that arrives while a walk is still sending is refused, so an outlet wired back into the inlet does not restart the walk.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
walk |
BANG, LIST |
— |
A bang walks the dictionary: one pair per entry out of the left outlet, then a bang out of the right one. So does the dictionary’s reference ‘dictionary <name>’. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
pair |
LIST |
— |
One entry per message: its whole ‘::’ path followed by its value, in the order the entries were first written. An entry with an empty value leaves as its path alone. |
1 |
done |
BANG |
— |
Bang after the last pair, also for an empty dictionary. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
name |
— |
any identifier |
The name of the dictionary to walk. Empty walks an empty dictionary: no pairs, only the done bang. |
.dict.join#
Merges two named dictionaries into a target dictionary: the left one’s entries, overlaid with the right one’s. Where both hold a key path, the right value wins.
- Dart:
Obj.gDictJoin- Kind:
Control-rate
The target gets every entry of the left dictionary, then every entry of the
right one. Where both hold the same key path, the right value replaces the
left one, so the left dictionary is the base and the right one the
override. Neither source is changed, unless it is also the target:
.dict.join base preset base merges preset into base.
When the result would be more than 256 entries, the new key paths of the right dictionary that do not fit are dropped; the rest of the join still happens.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
join |
BANG, LIST |
— |
A bang joins the two dictionaries into the target and sends the target’s reference. So does the left dictionary’s reference ‘dictionary <name>’. |
1 |
right reference |
LIST |
— |
Accepts the right dictionary’s reference and does nothing with it, so its .dict can be wired here. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
reference |
LIST |
— |
‘dictionary <target>’ after each join. Silent for an unnamed target. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
left |
— |
any identifier |
The name of the base dictionary. Empty joins an empty dictionary. |
right |
— |
any identifier |
The name of the dictionary laid over the base: its value wins on a shared key path. Empty joins an empty dictionary. |
target |
— |
any identifier |
The name of the dictionary that receives the result. Each join replaces its contents. It may be one of the two sources. |
.dict.pack#
Builds a named dictionary from its inlets, one inlet per key path: ‘.dict.pack synth freq gain’. The left inlet stores its value, writes the dictionary and sends its reference; the other inlets only store.
- Dart:
Obj.gDictPack- Kind:
Control-rate
The first argument names the dictionary, and every argument after it is a
key path with its own inlet. The inlets work like those of .pack (see
Lists): the left inlet stores its value and writes, the others only
store, so set the right-hand values first.
[.dict.pack synth freq gain] inlet 1 <- 0.5 inlet 0 <- 440
dictionary synth now holds: freq 440 gain 0.5
Messages
A value on any inlet is stored for that inlet’s key path. A whole list is one value, not spread over the inlets.
bang on the left inlet: write the dictionary without storing anything.
set <value>: store a value without writing.The reference
dictionary <name>of the bound dictionary writes it on the left inlet and is ignored on the others.
Writing replaces the whole dictionary with one entry per key path, so any other key in it is removed. A key path that has not received a value yet is written with an empty value. With no key paths, a bang writes an empty dictionary. When the same key path is given twice, the inlet further right wins.
At most 256 key paths of at most 128 characters; a longer path is logged and gets no inlet. A value longer than 256 characters is refused and the inlet keeps its old value.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
pack |
BANG, LIST |
— |
With no key paths, a bang writes an empty dictionary and sends its reference. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
reference |
LIST |
— |
‘dictionary <name>’ after each write. Silent for an unnamed dictionary. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
name |
— |
any identifier |
The name of the dictionary to write. Empty writes a private dictionary and sends nothing. |
keys |
— |
key paths |
One key path per inlet, in order. Paths nest with ‘::’. A path longer than 128 characters is logged and gets no inlet. |
.dict.print#
Prints a named dictionary to the engine log as indented JSON, one line per row, each line labelled with the dictionary’s name. A debugging aid with no outlets.
- Dart:
Obj.gDictPrint- Kind:
Control-rate
The dictionary is printed as indented JSON, the same structure a saved patch holds. Each line starts with the dictionary’s name and a colon:
synth: {
synth: "voice": {
synth: "1": {
synth: "freq": 440
synth: }
synth: }
synth: }
The lines reach the engine log the next time the host calls
System.instance.update(). Log.instance.logfile and
Log.instance.messages choose where the log goes.
Limits
The second argument limits how many lines the object sends per update,
64 by default and 1 to 256. When a dump runs past it, the rest is dropped and
one line says that the limit was reached. A line too long for the log is cut
and ends in .... Only the first 32 characters of the name are used as the
label.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
dump |
BANG, LIST |
— |
A bang prints the dictionary to the engine log. So does the dictionary’s reference ‘dictionary <name>’. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
name |
— |
any identifier |
The name of the dictionary to print, also used as the label in front of every line (at most 32 characters of it). Empty prints {} under the label ‘dict.print’. |
lines |
64 |
1-256 |
The most lines this object sends to the log per engine update. Lines past it are dropped and one notice line says so. A value that is not a number keeps the default. |
.dict.route#
Sends a named dictionary’s reference out of the outlet of the first key argument it holds, or out of the rightmost outlet when it holds none. Use it to send dictionaries of different shapes to different parts of a patch.
- Dart:
Obj.gDictRoute- Kind:
Control-rate
Each key argument has an outlet, and one more outlet on the right is added for no match. The dictionary is tested for each key in order, and its reference leaves the outlet of the first key it holds. Exactly one outlet sends per run.
A key matches an entry at that path or under it: voice matches
voice::1::freq, but not voicing. A key can be a deeper path such as
voice::1. When a key is given twice, only its first outlet is used.
The no-match outlet sends the same reference, so a second .dict.route
wired to it can test more keys. Without a name the object sends nothing,
because a private dictionary has no reference. A run that arrives while the
object is still sending is refused, so an outlet wired back into the inlet
does not loop.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
route |
BANG, LIST |
— |
A bang routes the dictionary: its reference leaves the outlet of the first key argument it holds, or the rightmost outlet when it holds none. So does the dictionary’s reference ‘dictionary <name>’. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
none |
LIST |
— |
The dictionary’s reference, when it holds none of the keys. Wire it into the next .dict.route to test more keys. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
name |
— |
any identifier |
The name of the dictionary to route. Empty makes the object do nothing. |
keys |
— |
key paths |
One key per outlet, in order. A key may be a path (‘voice::1’); it matches an entry at that path or under it. The rightmost outlet is always added for no match. |
.dict.serialize#
Sends a named dictionary as one line of JSON text. ‘write <file>’ saves the same JSON to a file instead, for a dictionary too large to send as a message.
- Dart:
Obj.gDictSerialize- Kind:
Control-rate
The JSON is written on one line with no spaces outside strings. :: paths
become nested objects, and the entries keep the order they were first
written. Values are written as:
one number: a JSON number;
several tokens: an array;
an empty value:
"";anything else: a string.
An empty dictionary is {}. .dict.deserialize reads this text back
into a dictionary.
Limits
A message holds at most 255 characters. When the JSON is longer, nothing is
sent. Use write <file> instead, which saves the same JSON to a file of up
to 128 KiB, or walk the entries with .dict.iter. A bare write writes
to the last file again. See File input and output.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
serialize |
BANG, LIST |
— |
A bang sends the dictionary as JSON text. So does the dictionary’s reference ‘dictionary <name>’. ‘write <file>’ saves the same JSON to a file instead; a bare ‘write’ writes to the last file again. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
json |
LIST |
— |
The dictionary as one line of compact JSON, in one list message. Nothing is sent when the text would be longer than 255 characters. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
name |
— |
any identifier |
The name of the dictionary to convert. Empty converts an empty dictionary, which gives {}. |
.dict.slice#
Splits a named dictionary at a key path. Entries under the path go into the slice dictionary with the path removed from their keys; all other entries go into the remainder dictionary.
- Dart:
Obj.gDictSlice- Kind:
Control-rate
An entry is under the path when its key starts with the path followed by
::. Those entries go into the slice dictionary with that start removed,
so the sub-tree becomes a dictionary of its own. Every other entry goes into
the remainder dictionary unchanged.
source: voice::1::freq 440 voice::1::gain 0.5 voice::2::freq 220
[.dict.slice synth v1 rest voice::1]
v1: freq 440 gain 0.5
rest: voice::2::freq 220
An entry stored at exactly the path is a value, not a sub-tree. It goes into the remainder.
voicedoes not matchvoices::1: the path ends at a::.An empty path matches nothing, so the remainder gets everything.
The remainder’s reference is sent first, then the slice’s. When both targets have the same name, that dictionary ends up holding the remainder.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
slice |
BANG, LIST |
— |
A bang splits the source dictionary into the slice and remainder dictionaries and sends both references. So does the source dictionary’s reference ‘dictionary <name>’. |
1 |
slice reference |
LIST |
— |
Accepts the slice dictionary’s reference and does nothing with it, so its .dict can be wired here. |
2 |
remainder reference |
LIST |
— |
Accepts the remainder dictionary’s reference and does nothing with it, so its .dict can be wired here. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
slice reference |
LIST |
— |
‘dictionary <slice>’ after each split, sent after the remainder. Silent for an unnamed slice dictionary. |
1 |
remainder reference |
LIST |
— |
‘dictionary <remainder>’ after each split, sent first. Silent for an unnamed remainder dictionary. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
source |
— |
any identifier |
The name of the dictionary to split. Empty splits an empty dictionary. |
slice |
— |
any identifier |
The name of the dictionary that receives the entries under the path, with the path removed. Each split replaces its contents. |
remainder |
— |
any identifier |
The name of the dictionary that receives every other entry, keys unchanged. Each split replaces its contents. |
path |
— |
any key path |
The key path to split at: ‘voice::1’ takes every ‘voice::1::…’ entry. Empty takes nothing. |
.dict.strip#
Removes every entry under a key path from a named dictionary, in place, then sends its reference. An entry stored at exactly the path is kept.
- Dart:
Obj.gDictStrip- Kind:
Control-rate
The entries removed are exactly the ones .dict.slice would put in its
slice for the same path: every entry whose key starts with the path followed
by ::. An entry at exactly the path is kept, and voice does not
match voices::1. An empty path removes nothing.
To remove one entry, send delete <path> to .dict; to remove
everything, send it clear.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
strip |
BANG, LIST |
— |
A bang removes every entry under the path and sends the dictionary’s reference. So does the dictionary’s reference ‘dictionary <name>’. |
Outlets
# |
Label |
Type |
Range |
Description |
|---|---|---|---|---|
0 |
dictionary reference |
LIST |
— |
‘dictionary <name>’ after each strip. Silent for an unnamed dictionary. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
name |
— |
any identifier |
The name of the dictionary to edit. Empty strips an empty dictionary. |
path |
— |
any key path |
The key path whose entries are removed: ‘voice::1’ removes every ‘voice::1::…’ entry. An entry at exactly the path is kept. Empty removes nothing. |
.dict.unpack#
Sends the values at a named dictionary’s key paths out of separate outlets, one outlet per key path argument, right to left. The inverse of .dict.pack.
- Dart:
Obj.gDictUnpack- Kind:
Control-rate
The first argument names the dictionary, and every argument after it is a
key path with its own outlet. Each value leaves its outlet right to left, so
a .dict.unpack can feed the cold inlets of an object before its hot one.
One number leaves as an int or a float, anything else as one list.
A missing path, or one holding an empty value, sends nothing on its outlet. When the same path is given twice, both outlets send. A run that arrives while the object is still sending is refused.
.dict.pack and .dict.unpack with the same name and key paths are a
pair: a reference from the .dict.pack makes the .dict.unpack send the
values that were packed. At most 256 key paths of at most 128 characters; a
longer path is logged and gets no outlet.
Inlets
# |
Label |
Accepts |
Range |
Description |
|---|---|---|---|---|
0 |
unpack |
BANG, LIST |
— |
A bang sends the value of each key path out of its outlet, right to left. So does the dictionary’s reference ‘dictionary <name>’. |
Parameters
Name |
Default |
Range |
Description |
|---|---|---|---|
name |
— |
any identifier |
The name of the dictionary to read. Empty reads an empty dictionary, so nothing is sent. |
keys |
— |
key paths |
One key path per outlet, in order. Paths nest with ‘::’. A path longer than 128 characters is logged and gets no outlet. |