Visibilily in #inject:into: block - smalltalk

This code:
((1 to: 10)
inject: (WriteStream on: String new)
into: [ :strm :each |
((each rem: 3) = 0)
ifTrue: [
strm
nextPutAll: each printString;
space;
yourself ]]) contents
fails because strm is undefined where it is used in the ifTrue: block. Why is it not visible there?
Edit: I tried it out in VASt and Pharo.

The problem is that the implied ifFalse: branch returns nil. To fix this, try the following:
((1 to: 10)
inject: (WriteStream on: String new)
into: [ :strm :each |
((each rem: 3) = 0)
ifFalse: [strm] "This is needed to avoid nil being returned"
ifTrue: [
strm
nextPutAll: each printString;
space;
yourself ]]) contents

Depending on the dialect (methods available), you can take much shorter approach
((1 to: 10) select: [ :each | (each rem: 3) = 0 ]) joinUsing: ' '
As a rule of thumb¹, any collection do: [ :each | something ifTrue: [] ] can be turned into much more straight-forward and readable collection select: [] or collection reject: []
Doing so will spread out the complexity over several independent steps (1. filtering, 2. adding to stream), instead of shoving it all together.
Or if you want to stick to your original
(((1 to: 10) select: [ :each | (each rem: 3) = 0 ])
inject: (WriteStream on: String new)
into: [ :stream :each |
stream
nextPutAll: each printString;
space;
yourself ]) contents
or
String streamContents: [ :stream |
(1 to: 10)
select: [ :each | (each rem: 3) = 0 ]
thenDo: [ :each |
stream
nextPutAll: each printString;
space
]
]
¹So not always, but always good to keep in mind when you encounter such situation.

Related

How do I get a table to display images

I am building a table in Seaside (Using VisualAge Smalltalk). I would like to have each table row have an image as well as text data. This what the method looks like:
where PSIni getImageUrl returns the main part of the url. The final url looks like
http://www.totallyobjects.com/images/pennstadt//15.jpg
Can anyone point me as to how to get this done please?
makeData: html usingMethod: aMethod
|tableData headingList methodList|
tableData := self perform: aMethod.
headingList := self headingList.
methodList := self methodList.
html table: [
html tableRow: [
headingList do: [ :each | html
tableHeading: each
]
].
tableData do: [ :one |
html tableRow: [
methodList do: [ :each |
each = #image ifTrue: [
html tableData: (self showImageFor: one id using: html)
] ifFalse: [
html tableData: (one perform: each)
]
]
]
]
]
(self showImageFor: one id using: html) obviously doesn't work. This method looks like this:
showImageFor: anID using: html
html image width: 200; url: PSIni getImageURL, '/', anID printString, '.jpg'
where PSIni getImageUrl returns the main part of the url.
The final url looks like:
http://www.totallyobjects.com/images/pennstadt/15.jpg
and should result in that image appearing in one element of a table row.
You need to nest the brushes:
html tableData: [ (self showImageFor: one id using: html) ]
It's explained in depth in the book: http://book.seaside.st/book/fundamentals/rendering-components

How do we iterate and select an element of a set in pharo?

My collection is a Set that contains a number of dictionaries. How can iterate over each dictionary in the Set to select a specific key.
a Set(a Dictionary('age'->'25' 'code'->2512) a Dictionary('age'->'40' 'code'->'1243') a Dictionary('age'->'35' 'code'->'7854'))
set := {
{ 'age'->'25'. 'code'->'2512' } asDictionary .
{ 'age'->'40'. 'code'->'1243' } asDictionary.
{ 'age'->'35'. 'code'->'7854' } asDictionary.
} asSet.
If you are interested in retrieving just a single item, then detect: is the way to go. It will return the first item matching the predicate (the block). Note that Set has no defined order, so if you have multiple items matching, it may return different ones at different time.
d := set detect: [ :each | (each at: 'code') = '1243' ].
d. "a Dictionary('age'->'40' 'code'->'1243' )"
If you want to retrieve multiple items that all match the predicate, then use select:
multi := set select: [ :each | (each at: 'age') asNumber >= 35 ].
multi. "a Set(a Dictionary('age'->'40' 'code'->'1243' ) a Dictionary('age'->'35' 'code'->'7854' ))"
Update from comment for commenting:
As Carlos already stated, collect: will do what you need. It applies the transformation block to every item in the collection and then returns a collection of results.
codes := set collect: [ :each | each at: 'code' ].
Works for any collection
#(2 3 4) collect: [ :each | each squared ] "#(4 9 16)"
For further I recommend going through the Collections chapter in Pharo By Example book https://ci.inria.fr/pharo-contribution/job/UpdatedPharoByExample/lastSuccessfulBuild/artifact/book-result/Collections/Collections.html
mySet do: [:each | each do: [ :i | i doStuff ]]
or use detect (I`m not sure if detect works like this, I never used it so far):
mySet do: [:i | i detect: [ :each| (each at: 'key') doStuff ]].
or use keysDo:
mySet do: [:each | each keysDo: [ :k | k doStuff ]]
Check out: http://pharo.gforge.inria.fr/PBE1/PBE1ch10.html

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.
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 ]
Taking Uko's solution a bit further:
forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
^ (aCollection
select: #positive
thenCollect: aBlock
) ifEmpty: defaultBlock
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.
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 ]

Rebol iterated face- truncating text

Using this code:
view layout [
t: text-list "this line truncated> lfkjsdflksjfslfsdjfsdlldskjflsdkfj" with [
text-pane: func [face id][
if pair? id [return 1 + second id / iter/size]
iter/offset: iter/old-offset: id - 1 * iter/size * 0x1
if iter/offset/y + iter/size/y > size/y [return none]
cnt: id: id + sn
if iter/text: pick data id [
iter/font/color: 255.0.0
lines: at data id
iter
]
]
]
]
All text beyond 'this line tuncated>' doesn't show up on the display window.
How do I get around this?
After a lot of painful digging here is how to NOT have the text-list truncate words from your
list. Add the "para: [ wrap?: false ]" line as shown below:
view layout [
t: text-list "this line truncated> lfkjsdflksjfslfsdjfsdlldskjflsdkfj" with [
text-pane: func [face id][
if pair? id [return 1 + second id / iter/size]
iter/offset: iter/old-offset: id - 1 * iter/size * 0x1
if iter/offset/y + iter/size/y > size/y [return none]
cnt: id: id + sn
if iter/text: pick data id [
iter/font/color: 255.0.0
lines: at data id
iter
]
]
para: [ wrap?: false ]
]
]

How to get all items in from of Text from Ordered collection

simple question i got
|list string|
list:= #('ab' 'efghij' 'lmnopqrst'). "Ordered collection"
list do:[:each| "string with:each" i know this is not right how do i add items ].
I tried streams too it returned me this "an ordered collection('ab' 'efghij' 'lmnopqrst')"
All i need is a single Text that has
'abc efghij lmnopqrst '
In Pharo you can do
Character space join: list
If join: is not available and it should perform well then you can use a stream variant
String streamContents: [:stream|
list
do [:each| stream nextPutAll: each ]
separatedBy: [ stream nextPut: Character space ]
Object class has defined a #asString message that reads:
"Answer a string that represents the receiver."
So, you can do:
| aList aStringsList |
aList := #('ab' 'efghij' 'lmnopqrst'). "Array"
aStringsList := aList collect: [ :each | each asString ]
And aStringsList will be an Array of the Strings returned by the invocation of #asString in each of aList's members.
If you want to concatenate all of them in a single String, you can use the #inject:into: method of the collections instead of #collect::
aList inject: '' into: [ :text :each | text , each asString , ' ' ]
If you print that you'll get the 'ab efghij lmnopqrst ' you want :)