Search code examples
stringselectintegersmalltalkpharo

In Smalltalk, how to select both strings and integers from an array


Using Pharo, I have a collection e.g.

array := #('up' '4' 'b').

and I'd like to use select: to create a collection that only includes the numbers, provided they're smaller than 20, and a specific string - in this case, 'up' and '4'

I tried:

array select: [:each | (each='up') | (each asInteger < 50)].  

This leads to a MessageNotUnderstood because receiver of "<" is nil.

I figured I'd have to create a local variable x:= each asInteger, but just couldn't work it out.


Solution

  • You're getting a MessageNotUnderstood because your code is trying to test whether 'b' asInteger (which is nil, because b isn't an integer) is < 20 (or < 50; you have different numbers in your text and your code). So all you need to do is test whether each array item is a number before you treat it as such.

    This should work in a Pharo workspace:

    | array |
    array := #('up' '4' 'b' '22').
    ^array select: [ :each | each = 'up'
        or: [ each isAllDigits and: [ each asInteger < 20 ] ] ]
    

    Inspecting the result of this gives #('up' '4') as expected.

    Note that I'm checking whether each string is "made up of all digits", and only doing the comparison if that is the case. Also note that I'm using or: and and:, which only evaluate the block argument if required, while | and & evaluate both sides regardless.

    You could also create a local variable, as you said you tried, but it would look a little clunkier (and I wouldn't call the variable x... unless it's an x-coordinate):

    | array |
    array := #('up' '4' 'b' '22').
    ^array select: [ :each |
        each = 'up' or: [
            | integerOrNil |
            integerOrNil := each asInteger.
            integerOrNil notNil and: [ integerOrNil < 20 ] ] ]