Send Feedback
Skip to content

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.