Entries in dictionary (3)
A table is a reference to a (column) dictionary.
The internal representation of a table is nearly identical to that of a dictionary. We can use the flip command to create a table from a dictionary:
q)t: ( a: 1 2 3; b: 4 5 6; c: 7 8 9) q)d: `a`b`c ! (1 2 3; 4 5 6; 7 8 9) q)t ~ flip d 1b q)
In fact, when a dictionary is
flip'ed, the underlying core data structure remains untouched. The table itself is a simple, small object that refers to the original dictionary. Using .Q.w, we can measure how much more memory a table takes than the corresponding dictionary:
q).Q.w`used // memory usage baseline 112992j q)x:`a`b`c!3 3#til 9 // create a small dictionary q).Q.w`used 113424j q)x:flip x q).Q.w`used // memory usage delta is 113456j // just 32 more bytes q)
No matter how large the underlying dictionary is, creating a table is fast and still takes only 32 bytes:
q)x:`a`b`c!3 100000#til 10 q).Q.w`used 1686192j q)x:flip x q).Q.w`used 1686224j // 32 = 1686224 - 1686192 q)
Now, Let's examine how a keyed table is related to a dictionary. We start by creating a simple keyed table:
q)t: ( a: 1 2 3; b: 4 5 6; c: 7 8 9) q)keyedTable: `a`b xkey t q)keyedTable a b| c ---| - 1 4| 7 2 5| 8 3 6| 9 q)keys keyedTable `a`b q)
keyedTable is a table, one might expect it to have the same type as t but instead, q presents the following surprise:
q)type t 98h // type table as expected q)type keyedTable 99h // *NOT* 98h q)
Type 99h is the type number for dictionaries. If
keyedTable really is a dictionary, we should be able to extract its
q)key keyedTable a b --- 1 4 2 5 3 6 q)value keyedTable c - 7 8 9 q)
keyedTable is a dictionary - one that holds unkeyed tables for both its key and its value:
q)type key keyedTable 98h q)type value keyedTable 98h q)
This suggests that we can create a keyed table by using the ! (dict) operator with two unkeyed tables:
q)(key keyedTable)!(value keyedTable) a b| c ---| - 1 4| 7 2 5| 8 3 6| 9 q)
Lastly, joining the two flipped tables brings us back to the original dict.
q)(flip key keyedTable),(flip value keyedTable) a| 1 2 3 b| 4 5 6 c| 7 8 9 q)
For more information, see Creating dictionaries and tables from C.
Short answer: A failed dictionary search returns the null corresponding to the first dictionary entry's value.
Let's test with the following two dictionaries, sfdict and fsdict:
q)sfdict: `symbol`float ! (`abc; 123.4) // type sym is first q)fsdict: `float`symbol ! (123.4; `abc) // type float is first q)sfdict symbol| `abc float | 123.4 q)fsdict float | 123.4 symbol| `abc q)
Note that while both dictionaries contain the same key-value pairs, because dictionaries are an ordered list of key value pairs, the above two dictionaries do not match.
q)sfdict = fsdict symbol| 1 float | 1 q)sfdict ~ fsdict 0b q)
A dictionary search returns a null value when the key sought is not found:
q)null sfdict[`badkey] 1b q)null fsdict[`badkey] 1b q)
Getting a null back from a failed lookup is expected. On the other hand, many are surprised to find that the two nulls returned for the above two lookups differ:
q)sfdict[`badkey] ~ fsdict[`badkey] 0b q)sfdict[`badkey] ` // null symbol q)fsdict[`badkey] 0n // null float q)
The types of the returned nulls are different according to the types of the first values in sfdict and fsdict. This is the same behavior observed when indexing a mixed list with an out-of-range index:
q)(`a; `b; 1f; 2f) ` q)(1f; 2f; `a; `b) 0n q)
There's a similar "first element wins" behavior with the atomicity of dictionary keys. see this faq on dictionary indexing to learn more.
Pass the key you want to remove as the left argument to the _ (drop) operator, and pass the dictionary as its right argument:
q)dict: `a`b`c ! 1 2 3
q)`b _ dict
If you reverse the order of the arguments, the removal still works:
q)dict _ `b
Although we can't think of a good reason to do this, you might come across it. As our commenter Attila pointed out, however, you are more likely to see this overload in its assignment form:
q)dict _: `b / same as dict: dict _ `b
Meanwhile, you can remove multiple keys at once by passing a list of keys as the left argument to _ (drop):
q)`a`c _ dict
Reversing the arguments does not work in this case:
q)dict _ `a`c