On Sun, Mar 21, 2010 at 12:57 AM, Tanaka A. wrote:
I think this kind of problems which slices consecutive elements in an array
is not well supported in Ruby.
On Sun, Mar 21, 2010 at 6:42 AM, Urabe S. wrote:
+1. There seems to be some real applications where that kind of tools are nifty.
On Sun, Mar 21, 2010 at 12:57 AM, Tanaka A. also wrote:
Enumerable#each_slice is not usable because it slices for each fixed number
of elements.
Ruby 1.9.2 has Enumerable#slice_before and it is usable but not so elegant
because it needs to maintain previous element.
…
We may need Enumerable#slice_between.
Is a really elegant method possible for this sort of thing?
Some time ago I was trying to find duplicate files,
and set up an array of files with elements (paths, sizes, checksums),
and then wrote an Enumerable#each_group method.
The only way I could think of differentiating a yield for “comparison”
from a yield of the wanted array of “similar” objects
was to have the first item of each yield to be a boolean
with true meaning compare, and false meaning it’s the wanted array.
I’d be interested in seeing a better Enumerable#each_group method,
if one is possible.
In the meantime, below is what I wrote for Enumerable#each_group,
with its application to Steve W.'s problem.
module Enumerable
Deeming successive enumerable objects to be “equivalent”
using a block compare, yields an array of the equivalent items.
def each_group_use_block_compare()
arr = nil ; obj_g = nil
self.each do | obj |
if arr then
# first item in yield is “true” indicating a yield for
comparison
if ( yield true, obj_g, obj ) == 0 then
arr << obj
obj_g = obj # group by adjacent objects, not by first in
group
next
end
# first item in yield is “false” indicating a yield of the group
yield false, arr
end
obj_g = obj ; arr = [ obj ]
end
if arr then
# first item in yield is “false” indicating a yield of the group
yield false, arr
end
return self
end
end
for the problem of Steve W.
def group_for_sw( arr )
arrg = []
arr.each_group_use_block_compare do | q_compare, aa, bb |
if q_compare then
if bb[1] - aa[1] < 30 then 0 else -1 end
else
aa.map! { |cc| cc[0] } # to preserve A, B, C objects, omit this
arrg << aa
end
end
arrg
end
arr = [ [ :A, 0 ], [ :B, 15 ], [ :C, 35 ],
[ :D, 100 ],
[ :E, 205 ], [ :F, 215 ],
[ :G, 300 ]
]
arrg = group_for_sw( arr )
p arrg #=> [[:A, :B, :C], [:D], [:E, :F], [:G]]