From: Jay Levitt Date: 2007-10-01T07:50:03+09:00 Subject: Re: Detecting number ranges 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 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 #=> # consecutives = s.divide {|i,j| (i - j).abs == 1} #=> #, #, #}> 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 -- 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