Object#tap (was Nice algorithm for 'spreading' indexes across anarray?)

H

Harry Kakueki

I have a situation where i have an array of 12 items. If someone
chooses to have n of them (where n can be between 3 and 12) then i want
to always include the first and last, and then 'spread' the others out
as evenly as possible between the rest.

So, lets say for the sake of argument that the array holds the numbers 1
to 12.
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

I would get results back like this

arr.spread(3)
=> [1,6,12] (or [1,7,12], either is fine)

arr.spread(4)
=> [1, 5, 9, 12] (or [1,4,8,12] or [1, 5, 8, 12])

It feels like there should be a simple solution for this but i can't
think of a nice way. Anyone?

thanks
max



Thanks to a post by David Masover in a recent thread, I learned about
Object#tap.
He used it on a hash.

I've used it here on an array.
Is there any problem using Object#tap in this way?
I saw some examples, but in the examples the object was not modified
in the block.



class Array
def spread(n)
dup.tap{|a| (size-n).downto(1).map{|b| size*b/(size-n+1)}.each{|c|
a.delete_at(c)}}
end
end

arr = (1..12).to_a
(3..12).each{|t| p arr.spread(t)}



###Output

[1, 6, 12]
[1, 4, 8, 12]
[1, 3, 6, 9, 12]
[1, 3, 5, 8, 10, 12]
[1, 2, 4, 6, 8, 10, 12]
[1, 2, 4, 6, 7, 9, 11, 12]
[1, 2, 3, 5, 6, 8, 9, 11, 12]
[1, 2, 3, 4, 6, 7, 8, 10, 11, 12]
[1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]


Harry
 
D

David Masover

Is there any problem using Object#tap in this way?
I saw some examples, but in the examples the object was not modified
in the block.

Oh, you mean modifying the array?
class Array
def spread(n)
dup.tap{|a| (size-n).downto(1).map{|b| size*b/(size-n+1)}.each{|c|
a.delete_at(c)}}
end
end

Not particularly. The thing you have to be careful of is reassigning it -- for
example, this wouldn't work:

1.tap {|x| x+=1}

You have to actually modify the object. For example, if we're talking about
arrays, instead of this:

string.tap {|x| x + 'foo'}

Do this:

string.tap {|x| x << 'foo'}

It seems like << returns self, so that's completely unnecessary, you could
just do this:

(string << 'foo')

...but hopefully it illustrates the point.

I have a feeling I'm overcomplicating things, though. Here's one possible
implementation of Object#tap:

class Object
def tap
yield self
self
end
end

Nothing mysterious about it at all. (MRI might do it in C, I'm not sure, but
the above works.)
 
G

Gavin Sinclair

Do this:

string.tap {|x| x << 'foo'}

A nice use of #tap (IMO, and these things are very much a matter of opinion):

message = String.new.tap { |s|
s << "..."
s << "..."
if some_condition?
s << "..."
else
s << "..."
end
end

Gavin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,223
Latest member
Jurgen2087

Latest Threads

Top