How to use iterators¶
This page explains how to use q's iterators to apply a function to each item of a list, in parallel, pairwise across two lists, and cumulatively, and how to repeat a function a fixed number of times, while a condition holds, or until its result stops changing.
An iterator takes a function and returns a new function, called a derived function, that applies the original in a particular pattern. count returns the length of a list; count each returns the length of every item in it. Iterators are how q expresses most of what other languages write as for and while loops, usually in a single short expression.
The iterators on this page, from simplest to most general, are:
| Iterator | Glyph | Keyword | Applies the function |
|---|---|---|---|
| Each | ' |
each |
to each item of a list |
| Each Parallel | ': |
peach |
to each item, spread across secondary threads |
| Each Both | ' |
to corresponding pairs of items from two lists | |
| Each Left, Each Right | \: /: |
between each item of one argument and the whole of the other | |
| Each Prior | ': |
prior |
between each item and the one before it |
| Scan, Over | \ / |
scan over |
cumulatively, carrying a result from one step to the next |
| Do, While, Converge | \ / |
repeatedly to its own result |
Before you iterate: atomic functions¶
Many q operators are atomic: they already apply to every item of a list, so they need no iterator at all.
q)1 2 3+10
11 12 13
Use an iterator only when a function does not already do what you want across a list. count, for example, is not atomic: it treats its argument as one list and counts its items.
q)count ("the";"quick";"brown";"fox")
4
q)count each ("the";"quick";"brown";"fox")
3 5 5 3
Do not iterate an atomic function
(2*) each 1 2 3 returns the same result as 2*1 2 3, but calls the function once per item instead of once for the whole list. See do not iterate atomic functions.
Each¶
each applies a unary function to each item of a list and returns a list of the results, one per item.
q
q)words:("the";"quick";"brown";"fox")
q)count each words
3 5 5 3
q){x where x>0} each (1 -2 3; -4 5)
1 3
,5
Python
>>> words = ["the", "quick", "brown", "fox"]
>>> list(map(len, words))
[3, 5, 5, 3]
>>> list(map(lambda xs: list(filter(lambda v: v > 0, xs)),
... ([1, -2, 3], [-4, 5])))
[[1, 3], [5]]
The keyword is a readable name for the Each iterator, '. The following three forms are equivalent, and good q style prefers the keyword for a unary function:
q)count each words
3 5 5 3
q)(count')words
3 5 5 3
q)count'[words]
3 5 5 3
each works on a dictionary too, applying the function to each value and keeping the keys:
q)d:([a:1 2 3; b:4 5; c:6 7 8 9])
q)sum each d
a| 6
b| 9
c| 30
To reach one level deeper into a nested list, apply each to a function that already uses each:
q)(count each) each (("ab";"cde");enlist "f")
2 3
,1
An empty list can change type
Each never calls the function on an empty list, so q cannot know what type the results would have had. It returns the generic empty list, of type 0h, rather than an empty list of the expected type:
q)type {2*x} each 0#0
0h
q)type 2*0#0
7h
Allow for this when the length of the list is not known in advance.
Each Parallel¶
peach computes the same result as each, but divides the items between secondary threads so that several calls run at once. Start q with secondary threads using the -s command-line option, and check the number with \s:
q -s 4
q)\s
4i
q){sum x*x} peach (til 5;til 10)
30 285
Without secondary threads, peach runs on the main thread and behaves exactly like each.
peach is faster when each call is independent and does enough work to outweigh the cost of handing it to another thread, for example reading separate files or processing separate partitions. A cheap function is usually slower with peach than with each. See peach in the performance guide before you rely on it for speed.
Like each, peach takes a unary function. To parallelize a function that takes more than one argument, project Apply (.) on it, so that each item supplies the full argument list:
q).[{x+y*z};] peach (1 2 3;4 5 6)
7 34
A secondary thread cannot update a global variable
Return results from the function and assign them on the main thread instead:
q){a::x} peach 1 2
'noupdate: `. `a
This restriction applies only when q has secondary threads. Without them, the same code runs without error, so test peach code in a process started with -s. For the other operations that peach blocks, and for its memory use, see each, peach.
Each Both¶
Applied to a binary function, the Each iterator ' pairs the items of its two arguments: the first item of the left list with the first item of the right list, and so on. This form is often called Each Both, and it works like a zip.
q
q)3 2 4#'("apple";"kiwi";"plum")
"app"
"ki"
"plum"
q)1 2 3,'10 20 30
1 10
2 20
3 30
Python
>>> list(map(lambda n, w: w[:n], [3, 2, 4],
... ["apple", "kiwi", "plum"]))
['app', 'ki', 'plum']
>>> list(zip([1, 2, 3], [10, 20, 30]))
[(1, 10), (2, 20), (3, 30)]
Unlike Python's map and zip, which stop at the end of the shorter list, q requires both lists to have the same length:
q)1 2,'10 20 30
'length
An atom on either side is paired with every item of the other:
q)"-",'("ab";"cd")
"-ab"
"-cd"
Each also applies functions of rank three or more. Write the arguments in brackets:
q){x+y*z}'[1 2;10 20;3 4]
31 82
Each Left and Each Right¶
Each Both pairs items one to one. Often you want instead to apply a function between each item of one argument and the whole of the other:
- Each Left
\:iterates over the left argument, and passes the whole right argument each time. - Each Right
/:iterates over the right argument, and passes the whole left argument each time.
q)"abc",\:"XY" / each of "abc", joined to "XY"
"aXY"
"bXY"
"cXY"
q)"abc",/:"XY" / "abc", joined to each of "XY"
"abcX"
"abcY"
Which side does the slash iterate?
The top of the slash leans toward the argument that q iterates. \ leans left, so \: iterates the left argument; / leans right, so /: iterates the right argument.
Each Right is the natural way to test one list against several values. Here each threshold is compared with all the prices, giving one row of booleans per threshold, and sum each counts the prices above each threshold:
q
q)prices:101 98 105 110
q)prices >/: 100 105
1011b
0001b
q)sum each prices >/: 100 105
3 1i
Python
>>> from itertools import repeat
>>> from operator import gt
>>> prices = [101, 98, 105, 110]
>>> [list(map(gt, prices, repeat(t))) for t in (100, 105)]
[[True, False, True, True], [False, False, False, True]]
>>> [sum(map(gt, prices, repeat(t))) for t in (100, 105)]
[3, 1]
Each Left suits a function that is not atomic in its left argument. ss finds the positions of a substring in one string, so Each Left applies it to each string in a list:
q)("hello";"yellow";"sky") ss\: "l"
2 3
2 3
`long$()
Each Left and Each Right are shorthand for a projection with each: x f\:y is f[;y] each x, and x f/:y is f[x;] each y.
Combine Each Left and Each Right¶
Apply both to pair every item of one argument with every item of the other. With join (,), the result shows each pair. The iterator written last, on the outside, determines how q groups the pairs:
q)1 2 3,/:\:10 20 / one row for each item on the left
1 10 1 20
2 10 2 20
3 10 3 20
q)1 2 3,\:/:10 20 / one row for each item on the right
1 10 2 10 3 10
1 20 2 20 3 20
Flatten the first form with raze and you get the Cartesian product, which q provides directly as the keyword cross:
q
q)1 2 3 cross 10 20
1 10
1 20
2 10
2 20
3 10
3 20
q)(raze 1 2 3,/:\:10 20)~1 2 3 cross 10 20
1b
Python
>>> from itertools import product
>>> list(product([1, 2, 3], [10, 20]))
[(1, 10), (1, 20), (2, 10), (2, 20), (3, 10), (3, 20)]
Use cross when you want the pairs themselves, for example every combination of symbol and date to query:
q)`AAPL`MSFT cross 2026.11.02 2026.11.03
`AAPL 2026.11.02
`AAPL 2026.11.03
`MSFT 2026.11.02
`MSFT 2026.11.03
Use the iterators when you want to apply some other function to every combination, and keep the results grouped. 1 2 3*/:\:10 20, for example, is a multiplication table with one row for each item on the left.
Atomic functions do not need Each Left or Each Right with an atom
1 2 3+10 already adds 10 to every item. The iterators matter when the other argument is a list that you want treated as a whole, as in 1 2 3+/:10 20. Without the iterator, q pairs the items and signals a length error because the lists differ in length.
Each Prior¶
Each Prior ': applies a binary function between each item of a list and the item before it. The keyword prior is its readable form, and deltas is the most common case, the difference between successive values:
q
q)(-':)100 102 101 105
100 2 -1 4
q)(-) prior 100 102 101 105
100 2 -1 4
q)deltas 100 102 101 105
100 2 -1 4
Python
>>> from itertools import chain
>>> from operator import sub
>>> p = [100, 102, 101, 105]
>>> list(map(sub, p, chain([0], p)))
[100, 2, -1, 4]
The function receives the current item as its left argument x, and the previous item as its right argument y. The first item has no previous item, so q supplies one: for - it uses 0, which leaves the first item unchanged. Drop the first result with 1_ when you only want the changes:
q)1_deltas 100 102 101 105
2 -1 4
See Each Prior for how q chooses the starting value for other functions.
Scan and Over¶
The iterators so far apply a function to items independently. Scan \ and Over / instead carry a result forward: they apply a binary function to the first two items, then to that result and the third item, and so on. Scan returns every intermediate result; Over returns only the last. Over is often called a reduce or fold in other languages.
q
q)(+\)1 2 3 4 5 / running total
1 3 6 10 15
q)(+/)1 2 3 4 5 / final total
15
Python
>>> from itertools import accumulate
>>> from functools import reduce
>>> from operator import add
>>> list(accumulate([1, 2, 3, 4, 5], add))
[1, 3, 6, 10, 15]
>>> reduce(add, [1, 2, 3, 4, 5])
15
The keywords scan and over are readable alternatives. Put the function in parentheses when it is an operator:
q)(+) scan 1 2 3 4 5
1 3 6 10 15
q)(+) over 1 2 3 4 5
15
Common combinations also have their own keywords, which good q style prefers: sums is +\, sum is +/, and maxs is |\, the running maximum.
q)(|\)3 1 4 1 5 9 2
3 3 4 4 5 9 9
q)maxs 3 1 4 1 5 9 2
3 3 4 4 5 9 9
Supply a starting value¶
Write a left argument to give Scan or Over a starting value. Here an account starts at 1000 and the right argument lists the deposits and withdrawals:
q
q)1000+\50 -20 30 / balance after each transaction
1050 1030 1060
q)1000+/50 -20 30 / closing balance
1060
Python
>>> list(accumulate([50, -20, 30], add, initial=1000))[1:]
[1050, 1030, 1060]
>>> reduce(add, [50, -20, 30], 1000)
1060
Note that Scan does not include the starting value in its result, whereas Python's accumulate does.
Use your own function¶
Scan and Over accept any binary function, which makes them the tool for a calculation in which each step depends on the one before. In the function, x is the result carried forward and y is the next item. This balance is not allowed to fall below zero, which a running sum alone cannot express:
q){0|x+y}\[0;5 -10 3 -1]
5 0 3 2
Over works on lists of any type, not only numbers. inter finds the items two lists have in common, so Over finds the items common to all of them:
q)(inter/)(1 2 3 4;2 3 4;3 4 5)
3 4
A function of rank three takes one extra list for each extra argument. Each step receives the carried result and the next item of each list. Here a trading position changes by each quantity multiplied by its side: 1 for a buy and -1 for a sell:
q){x+y*z}\[0;100 50 30;1 -1 1]
100 50 80
Replace Over with Scan to debug
When an Over returns a result you did not expect, change / to \. Scan performs the same computation and shows each intermediate result.
Over on an empty list
q does not call the function on an empty list. For an operator with a known identity, such as +, Over returns that identity; for a lambda, it returns the generic empty list:
q)(+/)0#0
0
q)()~{x+y}/[0#0]
1b
See empty lists for the full rules.
Do, While, and Converge¶
Scan and Over also accept a unary function. They then apply the function to its own result repeatedly, and the left argument determines when to stop:
| Form | Left argument | Stops |
|---|---|---|
| Do | an integer n |
after n applications |
| While | a unary test function | when the test returns false on the latest result |
| Converge | none | when the result stops changing |
As with binary functions, Scan \ returns every result, including the starting value, and Over / returns only the last. These forms replace most uses of the do and while keywords.
Do¶
With an integer on the left, q applies the function that many times. Here 100 grows by 5% a year for 3 years:
q
q)3 {x*1.05}\ 100f
100 105 110.25 115.7625
q)3 {x*1.05}/ 100f
115.7625
Python
>>> list(accumulate(repeat(None, 3),
... lambda x, _: x * 1.05, initial=100.0))
[100.0, 105.0, 110.25, 115.7625]
>>> reduce(lambda x, _: x * 1.05, repeat(None, 3), 100.0)
115.7625
The function can build up a list. In the following example, each step appends the sum of the last two numbers, so five steps extend 0 1 into the Fibonacci sequence:
q)5 {x,sum -2#x}/ 0 1
0 1 1 2 3 5 8
The rank of the function decides the meaning of the left argument
With a unary function, an integer on the left is a repeat count. With a binary function, the same integer is a starting value:
q)3 {x*2}/ 1 / unary: double 1, three times
8
q)3 +/ 1 2 / binary: 3 + 1 + 2
6
While¶
With a test function on the left, q keeps applying the function while the test is true of the latest result:
q
q){x<1000} {x*2}\ 1
1 2 4 8 16 32 64 128 256 512 1024
Python
>>> from itertools import takewhile
>>> list(takewhile(lambda x: x < 1000,
... accumulate(repeat(None), lambda x, _: x * 2,
... initial=1)))
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
The two results differ in their last item. 1024 fails the test, and Python's takewhile drops it, but q still returns it: q tests each result after computing it, and stops there. Write the test to look one step ahead if you do not want that result. Here a dictionary maps each person to their manager, and the test stops before the person with no manager:
q)boss:`ann`bob`cat!``ann`bob
q){not null boss x} boss\ `cat
`cat`bob`ann
The function being applied here is the dictionary boss itself: indexing a dictionary works like applying a function, so iterators accept dictionaries and lists too.
Converge¶
With no left argument, q applies the function until a result matches the previous one, or matches the starting value. This suits calculations that settle on an answer, such as Newton's method for the square root of 2:
q){(x+2%x)%2}\[1f]
1 1.5 1.416667 1.414216 1.414214 1.414214
q){(x+2%x)%2}/[1f]
1.414214
You do not need to know in advance how many steps it takes. Matching uses comparison tolerance, so floating-point results converge when they are close enough.
Converge is also the idiomatic way to repeat an operation until there is nothing left to do. raze removes one level of nesting, so raze/ flattens a list of any depth:
q)(raze/)(1;(2;(3;4)))
1 2 3 4
Following the manager chain from While with Converge runs until the lookup of the null name returns the null name again:
q)(boss\)`cat
`cat`bob`ann`
Converge does not stop if the result never repeats
(not/) 42 alternates between 0b and 1b forever, because no result matches the one before it or the starting value 42. Press Ctrl+C to interrupt it, and use Do or While when you cannot be sure a function converges.
Recursion is the other way to repeat a function on its own result, but q limits its depth. Iterators have no such limit; see recursive functions.
Combine iterators: FIFO allocation¶
Iterators combine, and a few of them together often replace a loop that would take a dozen lines in another language. This example, from Q for Mortals §1.14, fills a sell order from a time-ordered list of buys, first in, first out (FIFO): take from each buy in turn until the sell is filled, then stop.
q)buys:2 1 4 3 5 4
q)sell:12
The insight is to work with running totals rather than individual buys. sums, the Scan +\, gives the total available after each buy:
q)sums buys
2 3 7 10 15 19
The amount filled can never exceed the sell, so cap the running total at the sell amount with &, the lesser of the two:
q)sell&sums buys
2 3 7 10 12 12
That is the running total of the allocation. deltas, the Each Prior -':, turns a running total back into the individual amounts, and leaves the first item unchanged:
q
q)deltas sell&sums buys
2 1 4 3 2 0
Python
>>> from itertools import accumulate, chain
>>> from functools import partial
>>> from operator import sub
>>> buys = [2, 1, 4, 3, 5, 4]
>>> sell = 12
>>> filled = list(map(partial(min, sell), accumulate(buys)))
>>> list(map(sub, filled, chain([0], filled)))
[2, 1, 4, 3, 2, 0]
The first four buys are used in full, 2 is taken from the fifth, and the last is untouched. There is no loop, no counter, and no test for when to stop.
Fill orders from an order book¶
The same calculation fills orders against a market. A level 2 order book records, at each moment, the price and size available at the best few price levels on each side: bids from buyers and asks from sellers, each list ordered best price first. An order fills against the opposite side of the book, taking each level in turn until the order is filled. The levels play the part of the buys in the previous example, and the order quantity plays the part of the sell.
Here is a small book with seven snapshots for two symbols, recorded in time order as a market data feed would deliver them, and three orders. Each item of the bidpx, bidsz, askpx, and asksz columns is a list with one item per price level:
book:([]
sym:`AAPL`MSFT`AAPL`MSFT`AAPL`MSFT`AAPL;
time:09:30:00.000 09:30:00.000 09:30:00.600 09:30:00.700 09:30:01.000 09:30:01.100 09:30:01.500;
bidpx:(189.98 189.97 189.96; 420.05 420.00 419.95; 189.99 189.98 189.97; 420.10 420.05 420.00;
190.00 189.99 189.98; 420.15 420.10 420.05; 190.01 190.00 189.99);
bidsz:(300 500 200; 100 200 300; 200 300 400; 200 300 400; 100 400 300; 300 200 100; 200 300 300);
askpx:(190.01 190.02 190.03; 420.10 420.15 420.20; 190.02 190.03 190.04; 420.15 420.20 420.25;
190.02 190.03 190.04; 420.25 420.30 420.35; 190.03 190.04 190.05);
asksz:(200 400 600; 300 200 100; 100 300 500; 100 200 300; 300 300 500; 200 200 400; 400 200 300))
orders:([]
sym:`AAPL`AAPL`MSFT;
time:09:30:00.500 09:30:01.200 09:30:00.800;
side:`buy`sell`buy;
qty:500 600 800)
First, find the book each order met. An as-of join, aj, matches each order to the most recent snapshot of the same symbol at or before the order's time. It expects book to be sorted by time within each symbol, which a book recorded in time order is:
q)t:aj[`sym`time;orders;book]
q)delete bidpx,askpx from t
sym time side qty asksz bidsz
--------------------------------------------------
AAPL 09:30:00.500 buy 500 200 400 600 300 500 200
AAPL 09:30:01.200 sell 600 300 300 500 100 400 300
MSFT 09:30:00.800 buy 800 100 200 300 200 300 400
Each order picked up the latest snapshot for its symbol, not the nearest one. The AAPL buy at 09:30:00.500 matched the AAPL snapshot from 09:30:00.000, ignoring the later one at 09:30:00.600. The MSFT buy at 09:30:00.800 matched the MSFT snapshot from 09:30:00.700, and the AAPL sell at 09:30:01.200 matched the one from 09:30:01.000.
Next, pick the side of the book that each order trades against: asks for a buy, bids for a sell. The vector conditional ? chooses, row by row, which column supplies the whole list:
q)t:update px:?[side=`buy;askpx;bidpx], sz:?[side=`buy;asksz;bidsz] from t
q)select sym,time,side,qty,px,sz from t
sym time side qty px sz
-----------------------------------------------------------
AAPL 09:30:00.500 buy 500 190.01 190.02 190.03 200 400 600
AAPL 09:30:01.200 sell 600 190 189.99 189.98 100 400 300
MSFT 09:30:00.800 buy 800 420.15 420.2 420.25 100 200 300
Now apply the single-sell calculation, deltas sell&sums buys, to every row at once. Each row has its own quantity and its own list of sizes, so each step needs an iterator:
sums each szgives the running total of the sizes in each row.qty&'caps each row's running total at that row's quantity. This is Each Both: it pairs the first quantity with the first list, and so on.deltas eachturns each capped running total back into the amount taken from each level.
q)exec qty&'sums each sz from t
200 500 500
100 500 600
100 300 600
q)t:update fill:deltas each qty&'sums each sz from t
Finally, summarize the fills. sum each adds up the amount filled, and wavg with Each Both weights each row's prices by that row's fills to give the average price:
q)select sym,time,side,qty,fill,filled:sum each fill,avgpx:fill wavg'px from t
sym time side qty fill filled avgpx
------------------------------------------------------
AAPL 09:30:00.500 buy 500 200 300 0 500 190.016
AAPL 09:30:01.200 sell 600 100 400 100 600 189.99
MSFT 09:30:00.800 buy 800 100 200 300 600 420.2167
The AAPL buy is filled from the first two ask levels and leaves the third untouched. The MSFT buy wants 800, but the book shows only 600, so it fills 600 across all three levels. There is no loop over the orders and none over the price levels, so the same query works for any number of orders, symbols, and levels.
Choose an iterator¶
| You want to | Use | Example |
|---|---|---|
| Apply a function to each item | each |
count each words |
| Do the same in parallel | peach |
f peach files |
| Pair the items of two lists | ' |
3 2 4#'words |
| Apply between each left item and the whole right | \: |
words ss\: "l" |
| Apply between the whole left and each right item | /: |
prices>/:100 105 |
| Compare each item with the one before | prior, ': |
deltas prices |
| Keep a running result | scan, \ |
1000+\50 -20 30 |
| Reduce a list to one result | over, / |
(inter/) lists |
Repeat a function n times |
n f\x |
3 {x*1.05}\ 100f |
| Repeat while a condition holds | t f\x |
{x<1000} {x*2}\ 1 |
| Repeat until the result settles | (f\)x |
(raze/) nested |
Next steps¶
- Use iterators instead of loops in control execution, and learn when explicit control flow is still the right choice.
- Build the functions you pass to iterators with projections and operator projections.
- Parallelize work effectively with
peachand secondary threads. - Read the guide to iterators for more examples, including iterators on nested columns.
- Look up the full syntax in the reference: iterators, maps, accumulators, and
each,peach.