How to Work with Dictionaries
This page explains how to create and work with dictionaries in q.
A dictionary is an association between a list of keys and a list of values. Logically it can also be considered as key-value pairs but it is always stored as a pair of lists in q.
Creating a dictionary
A dictionary maps a list of keys to a corresponding list of values. The two lists must have the same count and the key list should be a unique collection. k!v returns a dictionary, where k is the key and v is the value. A dictionary can be decomposed into its key and value lists using the primitives key and value.
Example:
q
q)show d1:`Alice`Bob`Mike!30 25 43
Alice| 30
Bob | 25
Mike | 43
q)key d1
`Alice`Bob`Mike
q)value d1
30 25 43
q)type d1
99h
Python
>>> d1 = dict(zip(['Alice', 'Bob', 'Mike'], [30, 25, 43]))
>>> d1
{'Alice': 30, 'Bob': 25, 'Mike': 43}
>>> d1.keys()
dict_keys(['Alice', 'Bob', 'Mike'])
>>> d1.values()
dict_values([30, 25, 43])
>>> type(d1)
<class 'dict'>
Note
Dictionary keys should be unique (no duplicates), but q raises no error if duplicates are present. However, operations on dictionaries with duplicate keys are undefined.
Both the keys and the values can be nested:
q)(`a`b; 1 2 3; "Alice")!3 cut til 8
`a`b | 0 1 2
1 2 3 | 3 4 5
"Alice"| 6 7
If the keys are known to be unique, you can set the u attribute to them, as below. The dictionary will then function as a hash table, and indexing will be faster. The hash table uses storage and adds some overhead to the creation.
q)show d1:(`u#`Alice`Bob`Mike)!30 25 43
Alice| 30
Bob | 25
Mike | 43
q)key d1
`u#`Alice`Bob`Mike
Dictionary literal syntax
Dictionary literal syntax was introduced as part of kdb+ 4.1. You can concisely define dictionaries if the keys are symbols. It presents the mapping in a more human-readable way:
q
q)([Alice: 30; Bob: 25; Mike: 43])
Alice| 30
Bob | 25
Mike | 43
Python
>>> {'Alice': 30, 'Bob': 25, 'Mike': 43}
{'Alice': 30, 'Bob': 25, 'Mike': 43}
Literal syntax is particularly useful for singleton dictionaries:
q
q)enlist[`Alice]!enlist 30 / 4.0 syntax
Alice| 30
q)((), `Alice)!(), 30 / to save some typing
Alice| 30
q)([Alice:30]) / literal syntax
Alice| 30
Python
>>> {'Alice': 30}
{'Alice': 30}
Seeing keys and values next to each other is useful when creating larger dictionaries. Dictionary literal syntax can only be used with symbol keys, but you can use the over accumulator with ! for other types:
q
q)(!/) flip (("Alice"; 30); ("Bob"; 25); ("Mike"; 43))
"Alice"| 30
"Bob" | 25
"Mike" | 43
Python
>>> dict([("Alice", 30), ("Bob", 25), ("Mike", 43)])
{'Alice': 30, 'Bob': 25, 'Mike': 43}
Empty dictionaries
You can create a general empty dictionary using general empty lists:
q)()!() / general empty dictionary
but it is recommended to use typed dictionaries in production to prevent type promotion:
q)(`symbol$())!`float$() / typed empty dictionary
q)([])~(`symbol$())!() / empty dictionary with symbol keys
Projection from a dictionary literal syntax
Similar to list literal syntax (;..), omission of values results in projection:
q
q)d:([a:101; b:]) / missing values create projections
q)d 102
a| 101
b| 102
Python
>>> d = lambda x: {'a': 101, 'b':x}
>>> d(102)
{'a': 101, 'b': 102}
q)d each `AA`BB`CC / create a list of dictionaries (table) via projection
a b
------
101 AA
101 BB
101 CC
Implicit key names
Default key names are assigned when key names are omitted.
q)([0;1;2])
x | 0
x1| 1
x2| 2
Dictionary definition also makes it convenient to create a dictionary from variables.
q)Alice: 30
q)Bob: 25
q)([Alice; Bob])
Alice| 30
Bob | 25
Operations
Existence of a key
You can use function in to check the existence of a key.
q
q)d:([Alice: 30; Bob: 25; Mike: 43])
q)`Alice in key d
1b
q)`John in key d
0b
Python
>>> d = {'Alice': 30, 'Bob': 25, 'Mike': 43}
>>> 'Alice' in d
True
>>> 'John' in d
False
>>>
Lookup, indexing
To find the output value corresponding to an input key, you look up the key. Lookup uses the same notation as indexing into a list:
q
q)d:([Alice: 30; Bob: 25; Mike: 43])
q)d[`Bob] / indexing
25
q)d `Bob / brackets not necessary for indexing
25
q)d @ `Bob / using 'index at'
25
q)d `Mike`Alice / index by list
43 30
q)d `John / indexing out of domain returns a null of the same type as the first value
0N
Python
>>> d = {'Alice': 30, 'Bob': 25, 'Mike': 43}
>>> d['Bob']
25
>>> [d[k] for k in ('Mike', 'Alice') if k in d]
[43, 30]
In-place modification
You can modify the value of any key:
q
q)d:([Alice: 30; Bob: 25; Mike: 43])
q)d[`Alice]: 40 / update
q)d
Alice| 40
Bob | 25
Mike | 43
q)d[`John]: 30 / insert
q)d
Alice| 40
Bob | 25
Mike | 43
John | 30
q)d[`Alice`Bob]: 50
q)d
Alice| 50
Bob | 50
Mike | 43
John | 30
q)d[`Alice`Bob]: 60 70
q)d
Alice| 60
Bob | 70
Mike | 43
John | 30
Python
>>> d = {'Alice': 30, 'Bob': 25, 'Mike': 43}
>>> d['Alice'] = 40
>>> d
{'Alice': 40, 'Bob': 25, 'Mike': 43}
>>> d.update(dict.fromkeys(['Alice', 'Bob'], 50))
>>> d
{'Alice': 50, 'Bob': 50, 'Mike': 43}
>>> d.update({'Alice': 60, 'Bob': 70})
>>> d
{'Alice': 60, 'Bob': 70, 'Mike': 43}
You can also use the general amend at, if the dictionary name is provided as a symbol:
q)d:([Alice: 30; Bob: 25; Mike: 43])
q)@[`d;`Alice;:;50]
q)d
Alice| 50
Bob | 25
Mike | 43
Sub-dictionaries
You can extract sub-dictionaries by the take operator. The inverse opeartor is cut:
q
q)d:([Alice: 30; Bob: 25; Mike: 43])
q)`Mike`Alice#d / sub-dictionary
Mike | 43
Alice| 30
q)(enlist `Alice)#d
Alice| 30
q)`Mike`Alice cut d / underscore is also accepted
Bob| 25
Python
>>> d = {'Alice': 30, 'Bob': 25, 'Mike': 43}
>>> {k: d[k] for k in ('Mike', 'Alice') if k in d}
{'Mike': 43, 'Alice': 30}
>>> {'Alice': d['Alice']}
{'Alice': 30}
>>> {k: v for k, v in d.items() if k not in ('Mike', 'Alice')}
{'Bob': 25}
Arithmetic operations
Dictionaries are generalization of lists: the keys are predefined values rather than sequential integers. Most operators that work on lists also work on dictionaries.
Unary and binary arithmetic operations are executed elementwise:
q)d:([Alice: 30; Bob: 25; Mike: 43])
q)2*d
Alice| 60
Bob | 50
Mike | 86
q)d + ([Alice: 10; Bob: 5; John: 30])
Alice| 40
Bob | 30
Mike | 43
John | 30
q)neg d
Alice| -30
Bob | -25
Mike | -43
q)sums d
Alice| 30
Bob | 55
Mike | 98
q)asc d
Bob | 25
Alice| 30
Mike | 43
q)iasc d
`Bob`Alice`Mike
q)f: {2*x+1}
q)f d
Alice| 62
Bob | 52
Mike | 88
Aggregations, some set functions and some other functions only consider the values:
q
q)d:([Alice: 30; Bob: 25; Mike: 43])
q)count d
3
q)sum d
98
q)d except 25
30 43
Python
>>> d = {'Alice': 30, 'Bob': 25, 'Mike': 43}
>>> len(d)
3
>>> sum(d)
98
>>> [value for key, value in d.items() if value != 25]
[30, 43]
Recall that the keys are ordered, so you can reverse or take/drop first/last elements:
q
q)reverse ([Alice: 30; Bob: 25; Mike: 43])
Mike | 43
Bob | 25
Alice| 30
q)2 sublist ([Alice: 30; Bob: 25; Mike: 43])
Alice| 30
Bob | 25
q)-2 _ ([Alice: 30; Bob: 25; Mike: 43]) / drop elements
Alice| 30
Python
>>> dict(reversed({'Alice': 30, 'Bob': 25, 'Mike': 43}.items()))
{'Mike': 43, 'Bob': 25, 'Alice': 30}
>>> dict(list({'Alice': 30, 'Bob': 25, 'Mike': 43}.items())[:2])
{'Alice': 30, 'Bob': 25}
>>> dict(list({'Alice': 30, 'Bob': 25, 'Mike': 43}.items())[:-2])
{'Alice': 30}
Reverse lookup
Reverse lookups can be done on dictionaries using find:
q
q)d:([Alice: 30; Bob: 25; Mike: 43; John: 30])
q)d?25 / find
`Bob
q)d?40 / value not found works as for lists
` / returns a null of the same type as the first key
q)d?30 / only returns the first match
`Alice
Python
>>> d = {'Alice': 30, 'Bob': 25, 'Mike': 43, 'John': 30}
>>> next(k for k, v in d.items() if v == 25)
'Bob'
>>> next(k for k, v in d.items() if v == 30)
'Alice'
You can also use vector operation = and keyword where:
q
q)where d=25
,`Bob
q)where d=40
`symbol$()
q)where d=30
`Alice`John
Python
>>> [k for k, v in d.items() if v == 25]
['Bob']
>>> [k for k, v in d.items() if v == 40]
[]
>>> [k for k, v in d.items() if v == 30]
['Alice', 'John']
Joining dictionaries
The Join operator (,) merges two dictionaries.
Join on dictionaries has upsert semantics for common keys. If a key exists in both dictionaries, the value from the right-hand dictionary will overwrite the value from the left dictionary. New keys are appended to the end.
Example:
q
q)([Alice: 30; Bob: 25; Mike: 43]), ([Alice: 10; Bob: 5; John: 30])
Alice| 10
Bob | 5
Mike | 43
John | 30
Python
>>> {'Alice': 30, 'Bob': 25, 'Mike': 43} | {'Alice': 10, 'Bob': 5, 'John': 30}
{'Alice': 10, 'Bob': 5, 'Mike': 43, 'John': 30}
Coalesce ^
The coalesce operator ^ is related to , in that it employs upsert semantics to merge two dictionaries with right prevailing over left on common keys. The difference is that null values in the right operand do not prevail over the left.
q)left: ([Alice: 30; Bob: 0N; Mike: 43])
q)right: ([Alice: 10; Bob: 5; Mike: 0N])
q)left ^ right
Alice| 10
Bob | 5
Mike | 43
Column dictionaries
When a dictionary’s value items are all same-length lists, it is called a column dictionary. Column dictionaries are the foundation for tables.
Example:
q)show friends:([name:`Alice`Bob`Mike; dob: 1982.09.15 1984.07.05 1990.11.16; sex: "mfm"])
name| Alice Bob Mike
dob | 1982.09.15 1984.07.05 1990.11.16
sex | m f m
q)count each friends
name| 3
dob | 3
sex | 3
A column dictionary looks like a matrix, but you index by a key:
q)friends[`name; 1]
`Bob
q)friends . (`name;1)
`Bob
Flip a column dictionary and the result is a table:
q)flip friends
name dob sex
--------------------
Alice 1982.09.15 m
Bob 1984.07.05 f
Mike 1990.11.16 m
Step dictionaries
A step dictionary is a dictionary with the sorted attribute applied. When a key is not present, it returns the value associated with the nearest preceding key instead of a null value.
Note
The sorted attribute needs to be applied to the keys of the dictionary, as well as to the dictionary as a whole for it to function as a step dictionary. If the keys are unsorted, 's-fail will be thrown when you try to create a step dictionary.
An as-of join is an example of a step dictionary - it joins the prevailing value to the time field in the join.
Example:
q)d:(00:00:00; 04:00:00; 09:00:00)!`closed`preopen`open
q)ds:`s#d
q)d 06:00:00
`
q)ds 06:00:00
`preopen
Next Steps
- For additional details on dictionaries, check out Q for Mortals §5. Dictionaries
- List of dictionaries form a table. Carry on to the next section and learn about tables.