Tuesday, 17 November 2015

Enumerating using keysAndValuesDo:

**Part 1 - Displaying to the screen**
The most straight forward way to print to the screen in a GUI-based Smalltalk is to use the message:
`Transcript show: 'some text'`
(The Transcript is a system object that displays into a scrollable window).

To make sure there is a newline before each line of display text, we send the message `cr` to the Transcript

    Transcript cr.
    Transcript show: 'some text'.

A shorthand method, that saves us re-typing `Transcript` over and over, is to send Transcript a series of messages one after another.  This is called a *message cascade*.  Each time we end a message in `;` it means *send to the same receiver as the last message was sent to*.

We can then shorten this again, as Smalltalk pays no attention to newlines in expressions.

The final display message cascade becomes:
`    Transcript cr; show: 'some text'.`

**Part 2: Enumerating aSortedCollection using keysAndValuesDo:**
This keyword message is SequencableCollectionand its method header is:
`keysAndValuesDo: aBlock
"Enumerate the receiver with all the keys (aka indices) and values."`

(It works the same way in Dolphin, and in Squeak and its derivatives, Pharo and Cuis).

The keyword message `keysAndValuesDo:` takes a block argument.  
A block is an anonymous object, with one method.  Its method is defined between a matched pair of square brackets - a `[` ... `]` pair.

In this case, we need a local variable in the block for the key of each element of the collection, and another local variable for the value of each element.

We can call them anything we like, and in this case, it is the order that they appear in that is important.  `keysAndValuesDo:` will put the element's key into the first local variable in the block, and will put the element's value into the second local variable in the block.

Local variables in a block are declared at the start of the block, and each variable name is identified by prefixing it with `:`. The local variable declarations are ended with a `|`.

The block then looks like
`[:local1 :local2 |
 "do something for each element, with the key in local1 and the value in local2"

I prefer meaningful local variable names, so I'll use `eachKey` and `eachValue`.

**Part 3: Putting it all together**  
To enumerate through mySortedCollection

    "Declare the mySortedCollection variable"
    "Initialise the collection"
    mySortedCollection := SortedCollection new.
    "add in some data to the collection"
    mySortedCollection add: ('First') ;
                       add: ('Second') ;
                       add: ('Third').
    "Enumerate through the collection, displaying to the Transcript window"
    mySortedCollection keysAndValuesDo:
       [:eachKey :eachValue |
        Transcript cr; show: eachKey; show: ' '; show: eachValue
    ] .

Paste the code into a Workspace (known as a Playground in Pharo, from version 4.0 onwards).  Select the text.  Once selected, right-click (on a two or three button mouse) and select "Do it" from the menu. Or use Ctrl-d as a keyboard shortcut.  (The exact chording key may vary on your platform)

**Final notes**
In a SortedCollection or an OrderedCollection, the key is the index.  The value is what is stored at element[index].

In a Dictionary, the key of the element is the key, and the value of the element is the value.

SortedCollections are sorted in order of the element values, according to the definition of the collections *sort block*.  In the absence of a custom sort block, they will be added in ascending order.  `'First'`, `'Second'` and `'Third'` are, coincidentally, in alphabetical order.  It happens to work out nicely in this example.

No comments:

Post a Comment