Searching and indexing

Find values and patterns, remove repetitions, select items and order them

Read Nested arrays and trains first.

Membership and repeated values

∊ (Epsilon)

monadic ∊ (Enlist)

Enlist recursively collects all the atoms in nested arrays into one vector.

mat←[0 1 2⋄3 4 5]
mat
0 1 2
3 4 5
∊[0 mat [7 8] "nine"]
0 0 1 2 3 4 5 7 8 'n' 'i' 'n' 'e'

dyadic ∊ (Member of)

Membership tests whole items of a vector. Here neither "ab" nor "abcd" matches the string "abc".

["abc" 4]∊[4 "ab" "abcd"]
$f $t
mat∊6 2 7 4
$f $f $t
$f $t $f

Against a vector, membership tests each item of the left argument. Against a matrix, it tests each row:

[3 4 5⋄6 7 8]∊mat
$t $f

⍷ (Epsilon Underbar)

dyadic ⍷ (Find)

Find marks the start of each occurrence of a pattern. Occurrences can overlap, as in "ana" within "Banana".

1⍷3 1 4 1 5 9 2
$f $t $f $t $f $f $f
1 4⍷3 1 4 1 5 9 2
$f $t $f $f $f $f $f
"ana"⍷"Banana"
$f $t $f $t $f $f

A pattern can have more than one dimension:

pattern←[0 1⋄1 0]
board←[0 1 0 0⋄1 0 0 1⋄0 0 1 0⋄0 1 0 0]
[pattern board]
[[0 1⋄1 0] [0 1 0 0⋄1 0 0 1⋄0 0 1 0⋄0 1 0 0]]
pattern⍷board
$t $f $f $f
$f $f $t $f
$f $t $f $f
$f $f $f $f

monadic ⍷ (Groups)

Groups gives the positions of each distinct major cell, in order of first appearance:

⍷"Banana"
(0⋄1 3 5⋄2 4)ₓ

The lengths of those groups count the occurrences:

≢¨⍷"Banana"
[1 3 2]ₓ

∩ (Up shoe)

monadic ∩ (Duplicates)

Duplicates keeps one copy of each major cell that occurs more than once:

∩"mississippi"
isp

dyadic ∩ (Intersection)

Intersection keeps the major cells of the left argument found in the right argument. It preserves their order and repetitions.

"ABRA"∩"CAR"
ARA
[22 "ab" "fg"]∩['a' "ab" 22]
22 "ab"

∪ (Down shoe)

monadic ∪ (Unique)

Unique keeps the first of each distinct major cell:

a←2 3 4 2 3 4 5
∪a
2 3 4 5

Unique mask followed by Replicate gives the same result:

(≠a)#a
2 3 4 5
∪["ab" "ba" "ab" 1 1 2]
"ab" "ba" 1 2

dyadic ∪ (Union)

Union appends the major cells of the right argument that are absent from the left. Repetitions already present in either argument stay.

"ABRA"∪"CAR"
ABRAC
[22 "ab" "fg"]∪['a' "ab" 22]
22 "ab" "fg" 'a'

Sort / index

⌷ (Squad)

monadic ⌷ (Materialise)

⌷Y returns Y unchanged.

Its inverse also leaves the argument unchanged:

⌷⁻¹1 2 3
1 2 3

dyadic ⌷ (Index)

Indexing reads items at chosen positions. Those items form a selection. Each item of ⌷’s left argument selects along one axis, starting with axis 0. A vector of positions for a single axis is one item, hence the nested brackets in [[3 5]].

v←2×⍳12
3⌷v
6
[[3 5]]⌷v
6 10
mat←[0 1 2 3⋄4 5 6 7⋄8 9 10 11]
mat
0 1  2  3
4 5  6  7
8 9 10 11
2 3⌷mat
11

With two vectors, ⌷ takes every combination of the selected rows and columns:

[0 2;1 3]⌷mat
1  3
9 11

Bracket indexing

A dot before the brackets makes a selection. Positions count from 0; negative positions count from the end:

v.[3]
6

The outer brackets contain one index per axis. To select several positions along one axis, put a vector inside them:

v.[[3 5]]
6 10

.[] pairs its index arrays, rather than taking every combination. The same row and column positions now read two items:

mat.[[0 2] [1 3]]
1 11

Digit subscripts

A digit subscript selects one major cell along axis 0. v₁ is v.[1], and a negative subscript counts from the end. Consecutive digits form one position: ₁₂ selects position 12, and ₋₁₂ counts twelve places from the end. On a matrix it selects a row. A dot followed by a digit is a decimal point, so use v.[2] or v₂ for position 2:

v←10 20 30 40 50
[v₁ v₋₁]
20 50
mat₁
4 5 6 7

A subscript attaches to the preceding item before surrounding functions apply. Sum reads row 1 before adding its items. In v₁², item 1 is selected and then squared:

+/mat₁
22
v₁²
400

After a space, the subscript selects from the whole run on its left. It must stand alone, with a space on each side:

10 20 30 ₁
20
,mat ₁
1

An atom or scalar has no axis for a digit subscript. Use a subscript only when that position exists in an array.

Whole axes and new axes

Axes omitted at the end stay whole. A single position removes its axis, while an array of positions replaces that axis with the index array’s shape. Paired index arrays occupy the first index array’s place in the result, and whole axes keep their places:

mat.[1]
4 5 6 7
⍴mat.[[[0 1]⋄[1 2]]]
[2 2 4]ₓ

: takes one whole axis. Here the rows stay whole and columns 1 and 3 are selected:

mat.[: [1 3]]
1  3
5  7
9 11

Keep : as a separate item, as in mat.[: 1].

∞ takes all remaining axes at that position. At most one ∞ is allowed. Here it keeps both axes, so the result is mat:

mat.[∞]
0 1  2  3
4 5  6  7
8 9 10 11

A selector after ∞ applies at the end. Here ∞ keeps the rows and 1 selects the last axis’s column 1:

mat.[∞ 1]
1 5 9

An empty bracket list omits every axis, keeping the array unchanged:

mat.[]
0 1  2  3
4 5  6  7
8 9 10 11

¯∞ inserts a new unnamed axis of length 1, without consuming an input axis. Its position in the brackets decides where it goes:

⍴mat.[: ¯∞ :]
[3 1 4]ₓ
⍴mat.[∞ ¯∞]
[3 4 1]ₓ

Pairing and coordinates

Index arrays agree in shape as arithmetic arguments do. This selects column 2 from row 0, column 0 from row 1 and column 3 from row 2:

mat.[⍳3ₓ [2 0 3]]
2 4 11

Repeating the same index array selects a diagonal, with row and column positions equal:

i←⍳3ₓ
mat.[i i]
0 5 10

A coordinate lists positions along successive axes, such as [0 1] for row 0, column 1. An array of coordinates reads the item or cell at each. Where gives coordinates for a condition on a matrix:

where←⍸mat>8
where
(2 1⋄2 2⋄2 3)ₓ
mat.[where]
9 10 11

A coordinate array consumes the axes each coordinate covers. Any further index arrays pair with it. In this rank-3 array, two coordinates select rows, and the last index gives a column for each row:

cube←2 3 4⍴⍳24
coords←[[0 1] [1 2]]
cube.[coords [3 0]]
7 20

A path can continue into nested items, as Pick does. These paths select item 2 of item 1 and item 0 of item 0:

nested←[10 20;30 40 50]
nested.[[[1 2] [0 0]]]
50 10

Each path must end at a cell of the same shape. After paths go into nested items, :, ∞ and ¯∞ can keep or add axes of those cells:

nested←[[1 2 3⋄4 5 6] [7 8 9⋄10 11 12]]
paths←[[0 1] [1 0]]
nested.[paths]
4 5 6
7 8 9
⍴nested.[paths ∞ ¯∞]
[2 3 1]ₓ

Keys and dotted paths

A string selects a key. .name reads the literal key "name"; brackets can compute a key from a variable or expression. Dotted paths continue from left to right through nested records:

rec←["price":10 "qty":2 "address":["city":"LA"]]
rec.address.city
LA
field←"qty"
rec.[field]
2
rec.["pr","ice"]
10

T.(expression) evaluates an expression with T’s keys as names. A key’s name takes precedence over an outer name with the same spelling. Other names still refer to their surrounding definitions. Assignments inside the parentheses stay there, leaving T unchanged:

rec.(price×qty)
20

With keys on a matrix’s first axis, each name stands for a whole major cell:

table←"left" "right":[1 2 3⋄4 5 6]
table.(left+right)
5 7 9
rate←0.5
rec.(price×rate)
5
rec.(price←3)
rec.price
3
10

A name read by a key can hold a function too. After a function, however, a dot before another function makes an inner product, as in +.×.

ops←["double":2×]
ops.double 3
6

Keep a space before the argument when calling a function through a field. ops.double3 would read the key "double3".

Function-result selections and binding

After a function, .name and .[...] select from its result. Same, ⊢, returns its argument unchanged, so these read its fields and positions. The suffix also works after a dyadic function:

⊢.address.city rec
LA
⊢.[[1 3]] v
20 40
1 2 3 4 5 +.[[0 2]] v
11 33

A vector of records can be read field by field with Each. To evaluate an expression for every record, apply a dfn with Each; T.(...) itself binds the keys on T’s first axis, rather than mapping over records:

orders←[rec ["price":5 "qty":3]]
⊢.price¨orders
10 5
{⍵.(price×qty)}¨orders
20 15

Dotted paths and subscripts bind more tightly than functions. 2×rec.price doubles the price, and rec.address.city₁ reads character 1 of the city. A path is read left to right, with the array evaluated before its index:

2×rec.price
20
rec.address.city₁
'A'

Named indices

An index array can have a named axis. Indices with the same axis name pair; indices with different names keep separate axes and build every combination:

r←⍳["r":3] ⋄ c←⍳["c":4]
mat.[r c]
0 1  2  3
4 5  6  7
8 9 10 11

A named index that appears alone for its name selects the data axis with that name, wherever it stands in the brackets. Axis-qualified Iota gives an index with the axis’s name and keys:

sales←["city":"NY" "LA";"month":"Jan" "Feb" "Mar"]:[10 20 30⋄40 50 60]
cy←⍳⍠"city" sales ⋄ mo←⍳⍠"month" sales
sales≡sales.[mo cy]
$t

Strings select keys on any axis, and can mix with positions:

sales.[1 "Feb"]
50

A single string removes its axis. A one-item vector of strings keeps that axis, with length 1:

⍴sales.["LA"]
["month":3]ₓ
⍴sales.[["LA"]]
["city":1 "month":3]ₓ

When several selectors share a name, they pair and take the data’s axes in order instead. Both selectors below have the name "col": one gives a row for each column, and the other gives the column:

data←["row":3 "col":4]⍴mat
cols←⍳["col":4] ⋄ rows←["col":4]⍴2 0 1 2
data.[rows cols]
["col":4]⍴[8 1 6 11]

Reading fill and changing selections

Numeric positions must be whole numbers. A position outside an axis is an Index error. NaN means “not a number”, written $n; as a position it reads fill instead, zero for these numbers. Negative positions still count from the end:

v.[[¯1 $n 0]]
50 0 10

An assignment through a selection changes the original array. A single selection stores the whole value, while a vector selection replaces its selected items. Subscripts can be targets too, but a spaced subscript cannot:

v₁←5
v
10 5 30 40 50

Modified assignment, such as +←, adds to the existing values instead of replacing them. It applies once for each appearance of a repeated position. A NaN position writes nothing:

v.[[1 3 1 $n]]+←1
v
10 7 30 41 50
v₁+←1
v
10 8 30 41 50

Plain assignment to a repeated position keeps its last replacement:

v.[[0 0 $n]]←7 8 99
v
8 8 30 41 50

Index with ⌷ writes a block instead of pairing its indices. The space before ← makes the whole run on its left the target. A dotted target already holds its selection together:

grid←3 4⍴0
[0 2;1 3]⌷grid +←10
grid
0 10 0 10
0  0 0  0
0 10 0 10

Plain assignment through a missing key adds it, and creates missing records along a dotted path. Reading a missing key, modifying a missing key or writing past the end by position is an Index error:

rec.style.color←"red"
rec.style
["color":"red"]
rec.price+←2
rec.price
12
sales.["SF"]←70 80 90
sales.[: "Apr"]←1 2 3
sales
   Jan Feb Mar Apr
NY  10  20  30   1
LA  40  50  60   2
SF  70  80  90   3

Adding a row and column together fills the other new cells with zero:

sales.["TX" "May"]←9
sales
   Jan Feb Mar Apr May
NY  10  20  30   1   0
LA  40  50  60   2   0
SF  70  80  90   3   0
TX   0   0   0   0   9

A vector selection aligns replacement keys with its kept axis. Here the replacement gives the names in reverse order, but each new price goes to its own key. A single key selection instead stores the whole right argument as that item’s value:

prices←["aa":1 "bb":2]
prices.[["aa" "bb"]]←["bb":20 "aa":10]
prices
["aa":10 "bb":20]
prices.["aa"]←[7 8]
prices.aa
7 8

Repeated positions are allowed on unkeyed axes. A selection that repeats a key is a Domain error, because an axis cannot have duplicate keys.

⍒ (Grade Down)

monadic ⍒ (Grade Down)

Grade down gives the positions that would put the major cells in descending order. Reading those positions sorts the array, as > does directly.

⍒33 11 44 66 22
[3 2 0 4 1]ₓ
a←33 11 44 66 22
a.[⍒a]
66 44 33 22 11
[⍒a]⌷a
66 44 33 22 11
>a
66 44 33 22 11

Grade is useful when we sort one array by another. Here the names go in order of decreasing score:

names←"Sue" "Ali" "Bo"
scores←75 92 83
[⍒scores]⌷names
"Ali" "Bo" "Sue"

The inverse needs a permutation: the positions from 0 to one less than the length, each used once. It returns one vector whose Grade down is that permutation, rather than recovering the original values:

ranks←⍒⁻¹⍒a
ranks
[2 0 3 4 1]ₓ
⍒ranks
[3 2 0 4 1]ₓ

dyadic ⍒ (Dyadic Grade Down)

A character left argument gives a custom alphabet. Here c, then g, then f have increasing positions in that alphabet. Unlisted characters share a position after the listed ones. Grade down puts these positions in reverse order:

a←"abcdefgabcdefg"
b←"cgf"⍒a
[b]⌷a
abdeabdeffggcc

⍋ (Grade Up)

monadic ⍋ (Grade Up)

Grade up gives positions in ascending order. Ties keep their original order.

⍋33 11 44 66 22
[1 4 0 2 3]ₓ
a←33 11 44 66 22
a.[⍋a]
11 22 33 44 66
[⍋a]⌷a
11 22 33 44 66

Grade up’s inverse likewise turns a permutation into a vector having that grade:

ranks←⍋⁻¹⍋a
ranks
[2 0 3 4 1]ₓ
⍋ranks
[1 4 0 2 3]ₓ

dyadic ⍋ (Dyadic Grade Up)

With a custom alphabet, Grade up puts c, g, then f first:

a←"abcdefgabcdefg"
b←"cgf"⍋a
[b]⌷a
ccggffabdeabde

Sorting each column

A grade along a named axis keeps the other axis names. Here each column gets its own row positions. As a selector, the "row" axis selects rows and the "col" axis pairs those positions with the matching columns:

scores←["row":3 "col":4]⍴[3 1 4 1⋄5 9 2 6⋄5 3 5 8]
grades←⍋⍠"row" scores
grades
[0 0 1 0
 1 2 0 1
 2 1 2 2]ₓ
scores.[grades]
3 1 2 1
5 3 4 6
5 9 5 8

Previous: Nested arrays and trains · Study guide · Next: Rearranging and transforming arrays