Iteration Protocols

collect: answers a collection of the "same species" as the receiver (an Interval collects into an Array, a Set into a Set).

21.1 The core iterators

#(1 2 3) do: [:x | Transcript show: x printString] #(1 2 3) collect: [:x | x * 10] #(1 2 3 4) select: [:x | x even] elements not satisfying #(1 2 3 4) reject: [:x | x even] first match (error if none) #(1 2 3) detect: [:x | x > 1] first match or default

#(1 2 3) detect: [:x | x > 5] ifNone: [0] #(1 2 3) inject: 0 into: [:sum:x | sum + x] #(1 2 3) anySatisfy: [:x | x > 2] #(1 2 3) allSatisfy: [:x | x > 0] #(1 2 3 4) count: [:x | x odd]

#(1 2) with: #(10 20) collect: [:a:b | a + b] #(1 2) with: #(3 4) do: [:a:b |...] doWithIndex: / withIndexDo: #(a b) withIndexDo: [:e:i |...] dict keysAndValuesDo: [:k:v |...] #(1 2 3) reverseDo: [:x |...] #(1 2 3) do: [...] separatedBy: [...]

collect: answers a collection of the "same species" as the receiver (an Interval collects into an Array, a Set into a Set).

21.2 More useful iterators (Pharo)

#(1 2 3 4) sum.                                  "→ 10 (also sum: aBlock)"
#(1 2 3 4) detectMax: [ :x | x \\ 3 ].           "→ 2"
#(3 1 2) max.     #(3 1 2) min.
#(1 2 3 4 5 6) groupedBy: [ :x | x even ].       "→ a Dictionary(false->#(1 3 5) true->#(2 4 6))"
#(#(1 2) #(3 4)) flatCollect: [ :pair | pair ].  "→ #(1 2 3 4)"
#(1 2 3 4) collect: [ :x | x * x ] thenSelect: [ :x | x > 4 ].   "→ #(9 16)"
#(1 2 3 4) select: [ :x | x even ] thenCollect: [ :x | x * 10 ].  "→ #(20 40)"
#(1 2 3) allButFirst.  #(1 2 3) allButLast.  #(1 2 3 4) first: 2.  #(1 2 3 4) last: 2.
#(1 2 3) ifEmpty: [ 'none' ] ifNotEmpty: [ :c | c size ].
#(1 2 3) isEmpty.  #(1 2 3) notEmpty.  #() isEmptyOrNil.  #(1 2 2 3) occurrencesOf: 2.

21.3 inject:into: in depth

inject:into: (a *fold* or *reduce*) threads an accumulator through the collection:

#(3 4 5) inject: 0 into: [ :acc :each | acc + each ].
"acc = 0 → 3 → 7 → 12"

"Build a dictionary of word frequencies"
('the cat and the hat' substrings) inject: Dictionary new into: [ :dict :word |
    dict at: word put: (dict at: word ifAbsent: [ 0 ]) + 1.
    dict ].

"Factorial with inject"
(1 to: 10) inject: 1 into: [ :product :n | product * n ].     "→ 3628800"

21.4 Never modify a collection while iterating it

"WRONG: removing during do: skips elements or errors"
oc do: [ :x | x even ifTrue: [ oc remove: x ] ].
"RIGHT"
oc removeAll: (oc select: [ :x | x even ]).
oc := oc reject: [ :x | x even ].
oc copy do: [ :x | x even ifTrue: [ oc remove: x ] ].

21.5 Internal vs external iteration

Smalltalk collections iterate internally (you hand a block to the collection). For external, step-by-step iteration, use a stream (readStream) — Chapter 23. Lazy, pipeline-style iteration is available via libraries such as *Iterators* or *Generator* (Generator on: [ :g | ... g yield: x ] in Pharo).

21.6 Writing your own iterator

Subclass Collection and implement do: (plus add:/remove:ifAbsent:) to inherit every other iterator — or, for a domain object such as a Playlist, keep an internal OrderedCollection and delegate do:, select:, and friends to it.