Uko
Uko

Reputation: 13396

Perform block for each satisfied condition, otherwise perform other block

Imagine that you have to select some values and for each of them you have to evaluate a block. On the other hand if there is no value that satisfies the condition another block has to be evaluated.

Example: Consider the next method signature:

forPositivesOf: aCollection do: aBlock otherwise: defaultBlock

This method should evaluate a block aBlock with every positive element of aCollection, but if there are no elements like that, evaluate defaultBlock. Please note that in reality the method may calculate something more complex than just positive numbers, and instead of aCollection there can be a much complex object.

Upvotes: 4

Views: 108

Answers (4)

blabla999
blabla999

Reputation: 3200

Taking Uko's solution a bit further:

forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
    ^ (aCollection
          select: #positive
          thenCollect: aBlock
      ) ifEmpty: defaultBlock

Upvotes: 1

Uko
Uko

Reputation: 13396

One really nice functional way that works for SequenceableCollections:

forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
  (aCollection
    select: #positive
    thenCollect: [ :el | aBlock cull: el ]) ifEmpty: [ defaultBlock value ]

Upvotes: 0

Esteban A. Maringolo
Esteban A. Maringolo

Reputation: 1218

A more compact version of the first alternative is the following that doesn't instantiate a new closure, and just uses the ones received as arguments.

forPositivesOf: aCollection do: aBlock otherwise: defaultBlock

    ^(aCollection select: [:each | each positive ]) 
         ifEmpty: defaultBlock
         ifNotEmpty: [ :collection | collection do: aBlock ]

Upvotes: 4

Uko
Uko

Reputation: 13396

At the moment I see two solutions:

1)

forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
  (aCollection select: #positive)
    ifEmpty: [ defaultBlock value ]
    ifNotEmpty: [ :collection |
      collection do: [ :el | aBlock cull: el ] ]

but in case calculation of positive is expensive it would be good to evaluate aBlock for the first encountered element, as then the one who passed aBlock will be able to react in any desired way.

2)

forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
  | encountered |
  encountered := false.
  aCollection do: [ :el |
    el positive ifTrue: [
      encountered := true.
      aBlock cull: el ] ].

  encountered ifFalse: [
    defaultBlock value ]

But I don't like the extra encountered variable, it makes code less functional.

Upvotes: 1

Related Questions