That's beautiful! I *love* ranger. And there are a lot of other techiques
I learned from here. Again, I've oversimplified it below for future
reference.
I've never seen "set.divide" before, and it's the key to the whole thing.
Question: Isn't it possible to give set.divide a block that'll give
inconsistent or crazy results? I can't quite think of how, but it's
assuming that the relationships are commutative..
It's also really interesting that I was initially using the group_by from
version 3726 of Rails:
def group_by
inject() do |groups, element|
value = yield(element)
if (last_group = groups.last) && last_group.first == value
last_group.last << element
else
groups << [value, [element]]
end
groups
end
end
which resulted in two separate "server 7" groups - yet something later in
the chain healed that rift.
Finally, the function I broke out as "text_range" felt like an Escher
drawing at first...
def text_range(ary)
"#{ary.first}#{" to #{ary.last}" if ary.size>1}"
end
But I finally realized that the braces must take precedence over the
quotes, so the middle quotes are "inner" quotes, as if it read:
def text_range(ary)
s = "#{ary.first}"
s << " to #{ary.last}" if ary.size > 1
s
end
# botp.rb
require 'set'
# Using server 7 as our example
module Enumerable
def ranger
s = to_set
#=> #<Set: {33, 30, 8, 31, 20, 9, 32, 10}>
consecutives = s.divide {|i,j| (i - j).abs == 1}
#=> #<Set: {#<Set: {20}>, #<Set: {8, 9, 10}>,
#<Set: {33, 30, 31, 32}>}>
inner_sorted = consecutives.map{|group| group.to_a.sort}
# each group is sorted internally now
#=> [[30, 31, 32, 33], [20], [8, 9, 10]]
inner_sorted.sort_by{|s| s.first}
# now groups are in sorted order as well
#=> [[8, 9, 10], [20], [30, 31, 32, 33]]
end
def group_by
inject({}) do |groups, element|
(groups[yield(element)] ||= ) << element
groups
end
end if RUBY_VERSION < '1.9'
end
def text_range(ary)
# Note the quoted text - and another #{} - *inside*
# the #{... if ary.size>1} construct.
# Who knew you could do such a thing?
"#{ary.first}#{" to #{ary.last}" if ary.size>1}"
end
db_server_pairs = DATA.readlines.map{|s| s.scan(/\d+/).map{|s| s.to_i}}
#=> [[8, 7], [10, 7], [9, 7], ... ]
pairs_by_server = db_server_pairs.group_by{|pair| pair.last}
#=> [[7, [[8, 7], [10, 7], [9, 7], ... ]], 3, ... ]
puts "Server\tDatabases"
pairs_by_server.sort.each do |server, pairs|
# server => 7
# pairs => [[8, 7], [10, 7], [9, 7], ...]
my_dbs = pairs.map {|pair| pair.first}
#=> [8, 10, 9, ... ]
my_ranges = my_dbs.ranger
#=> [[8, 9, 10], [20], [30, 31, 32, 33]]
range_text = my_ranges.map{|r| text_range(r)}.join(", ")
puts "#{server}\t#{range_text}"
end
__END__
database 8 is on server 7
database 10 is on server 7
database 9 is on server 7
database 5 is on server 9
database 1 is on server 3
database 2 is on server 3
database 133 is on server 3
database 4 is on server 144
database 3 is on server 144
database 20 is on server 7
database 30 is on server 7
database 31 is on server 7
database 32 is on server 7
database 33 is on server 7
···
On Fri, 28 Sep 2007 17:30:46 +0900, Peña, Botp wrote:
i was impressed by James and Raf's solutions, so i combined the two, together using 1.9's group_by, so
--
Jay Levitt |
Boston, MA | My character doesn't like it when they
Faster: jay at jay dot fm | cry or shout or hit.
http://www.jay.fm | - Kristoffer