Collections

Beamtalk's core collections:

All collections are immutable — "mutating" operations return a new collection.

Array

Literal syntax: #[element, element, ...]

#[1, 2, 3]          // => #[1, 2, 3]
#[]                 // => #[]
#["hello", "world"] // => #["hello", "world"]

Size:

#[1, 2, 3] size  // => 3
#[] isEmpty      // => true
#[1] isEmpty     // => false

Indexed access (1-based):

#[10, 20, 30] at: 1  // => 10
#[10, 20, 30] at: 3  // => 30

Functional update (returns a new array, original unchanged):

a := #[1, 2, 3]     // => _
a at: 2 put: 99     // => #[1, 99, 3]
a                   // => #[1, 2, 3]

Check membership:

#[1, 2, 3] includes: 2  // => true
#[1, 2, 3] includes: 9  // => false

Note: Array is immutable and fixed-size. Methods like add:, sort, and reversed are available on List (#(...)) which also supports iteration. See the List class for ordered sequence manipulation.

Iteration

do: — side-effects only (returns nil):

#[1, 2, 3] do: [:x | x * 2]  // => _

collect: — transform each element (returns new array):

#[1, 2, 3] collect: [:x | x * 2]                   // => #[2, 4, 6]
#["hello", "world"] collect: [:s | s uppercase]     // => #["HELLO", "WORLD"]

select: — keep elements matching a predicate:

#[1, 2, 3, 4, 5] select: [:x | x > 3]   // => #[4, 5]
#[1, 2, 3, 4, 5] select: [:x | x isEven]  // => #[2, 4]

reject: — opposite of select::

#[1, 2, 3, 4, 5] reject: [:x | x isEven]  // => #[1, 3, 5]

detect: — find the first matching element (error if none):

#[1, 2, 3, 4, 5] detect: [:x | x > 3]  // => 4

detect:ifNone: — safe version:

#[1, 2, 3] detect: [:x | x > 10] ifNone: [nil]  // => nil

inject:into: — fold/reduce:

#[1, 2, 3, 4, 5] inject: 0 into: [:sum :x | sum + x]     // => 15
#[1, 2, 3, 4] inject: 1 into: [:product :x | product * x]  // => 24

allSatisfy: — true if all elements match:

#[2, 4, 6] allSatisfy: [:x | x isEven]  // => true
#[2, 3, 6] allSatisfy: [:x | x isEven]  // => false

anySatisfy: — true if any element matches:

#[1, 3, 4] anySatisfy: [:x | x isEven]  // => true
#[1, 3, 5] anySatisfy: [:x | x isEven]  // => false

Dictionary

Literal syntax: #{key => value, key => value}

Keys are typically symbols but can be any value.

d := #{#name => "Alice", #age => 30}  // => _

Access by key:

d at: #name  // => Alice
d at: #age   // => 30

Safe access with default:

d at: #missing ifAbsent: ["default"]  // => default

Check if a key exists:

d includesKey: #name     // => true
d includesKey: #missing  // => false

Adding/updating (returns new dictionary):

d2 := d at: #city put: "Dublin"  // => _
d2 at: #city                     // => Dublin
d includesKey: #city             // => false

All keys and values:

d keys    // => _
d values  // => _
d size    // => 2

Iteration:

d keysAndValuesDo: [:k :v | k]  // => _

Bag (multiset)

A Bag is an unordered collection that allows duplicate elements.

b := Bag withAll: #(1, 2, 1, 3, 1)  // => _
b size                               // => 5
b occurrencesOf: 1                   // => 3
b occurrencesOf: 2                   // => 1
b occurrencesOf: 99                  // => 0
b includes: 2                        // => true

Add an element (returns new Bag):

b2 := b add: 2        // => _
b2 occurrencesOf: 2   // => 2
b occurrencesOf: 2    // => 1

Set

A Set is an unordered collection with no duplicates.

s := Set withAll: #(1, 2, 1, 3, 2, 1)  // => _
s size                                   // => 3
s includes: 1                            // => true
s includes: 99                           // => false

Adding to a Set (returns new Set):

s2 := s add: 4  // => _
(s2 includes: 4)  // => true

Adding a duplicate is a no-op:

s3 := s add: 1  // => _
s3 size         // => 3

Converting between collection types

List to Set (removes duplicates):

(Set withAll: #(1, 2, 1, 3)) size  // => 3

List to Bag:

(Bag withAll: #(1, 1, 2)) occurrencesOf: 1  // => 2

Ranges

Ranges are produced by to: and support iteration (chapter 8), but can also be collected into arrays:

(1 to: 5) collect: [:n | n]        // => [1,2,3,4,5]
(1 to: 10 by: 2) collect: [:n | n]  // => [1,3,5,7,9]

Summary

Array: #[1, 2, 3] — immutable, indexed, fixed-size

Dictionary: #{#k => v}

Bag: Bag withAll: #[...]

Set: Set withAll: #[...]

Exercises

1. Find the maximum. Use inject:into: to find the maximum value in #[3, 1, 4, 1, 5, 9, 2, 6].

Hint
#[3, 1, 4, 1, 5, 9, 2, 6] inject: 0 into: [:max :x |
  (x > max) ifTrue: [x] ifFalse: [max]
]
// => 9

2. Dictionary filtering. Create a dictionary mapping names to ages: #{#alice => 28, #bob => 17, #carol => 35}. Extract just the values and use select: to find ages over 21.

Hint
people := #{#alice => 28, #bob => 17, #carol => 35}
people values select: [:age | age > 21]    // => contains 28 and 35

3. Remove duplicates. Use Set withAll: to remove duplicates from #(1, 2, 2, 3, 3, 3, 4, 4, 4, 4). How many unique elements remain?

Hint
s := Set withAll: #(1, 2, 2, 3, 3, 3, 4, 4, 4, 4)
s size    // => 4

The Set keeps only unique values: 1, 2, 3, 4.

Next: Chapter 10 — Value Classes