Rubyize This: Live in Vancouver. Refactoring #2
Here’s the second refactoring from the Rubyize This workshop. See the first refactoring for an explanation of what’s going on and why this code is so darn ugly! Don’t forget to check out the third and final refactoring
This script loads the file full of random numbers from the first refactoring and makes a beautiful ascii-art histogram from it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#!/usr/bin/env ruby require 'pp' BIG_NUMBER = 32768 # Get data from a file, turn it in to a float, and find the max data = File.readlines('random.txt') max = -BIG_NUMBER for n in (0 ... data.length) data[n] = data[n].chomp.to_f if data[n] > max max = data[n].to_i + 1 # max is ceil(max(data[n])) end end # Create the empty histogram histogram = [] for n in (0 .. max) histogram.push(0) end # Fill the histogram for n in (0 ... data.length) histogram[data[n].to_i] += 1 end # Print the histogram pp histogram puts for n in (0 .. max) puts "*" * histogram[n] end |
Here’s a very concise one-liner from the crowd:
0.upto((data=File.readlines('random.txt').collect {|e| e.chomp!.to_f}).max.to_i) {|i| puts i.to_s + " " + data.select{|a| a==i}.size.to_s} |
Here’s Owen’s refactoring:
1 2 3 4 5 6 7 8 9 10 11 12 |
require 'pp' histogram = [] File.readlines('random.txt').each do |value| i = value.chomp.to_i histogram[i] ||= 0 histogram[i] += 1 end # Print the histogram pp histogram puts histogram.each { | v | puts "*" * v } |
Here’s Sam Livingstone Gray’s refactoring
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/usr/bin/env ruby require 'pp' # Get data from a file, turn it in to a float, and find the max module Enumerable def value_counts h = Hash.new(0) each { |e| h[e] += 1 } h end end lines = File.readlines('random.txt') histogram = lines.map { |line| line.chomp.to_i }.value_counts # Print the histogram pp histogram puts histogram.keys.sort.each { |n| puts '*' * histogram[n] } |
I really like this one. Sam created a simple extension to Enumerable that I can see using over and over again.
Here’s what I came up with, with some debugging help from the group
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/usr/bin/env ruby require 'pp' data = File.readlines('random.txt').collect {|datum| datum.chomp.to_f} max = data.max histogram = data.inject([0] * (max + 1)) do |histogram, datum| histogram[datum.to_i] += 1 histogram end pp histogram histogram.each do |bucket| puts "*" * bucket end |
Comments
-
oneliner trial: 0.upto((data=File.readlines('random.txt').collect {|e| e.chomp!.to_f}).max.to_i) {|i| puts i.to_s + " " + data.select{|a| a==i}.size.to_s}
-
oops, should read: 0.upto((data=File.readlines('random.txt').collect {|e| e.chomp!.to_f}).max.to_i) {|i| puts "*" * data.select{|a| a==i}.size}
-
there's a bug in this code -- which is why i wasn't able to get it working today. line 19 should read: for n in (0 .. max) if you use the exclusive range operator, it will fail because the histogram array will not initialise all of the necessary values to 0.
-
similarly, the exclusive range operator should not used on line 31 either. this is my solution. it could probably be compressed further, but at the expense of readability: require 'pp' histogram = [] File.readlines('random.txt').each do |value| i = value.chomp.to_i histogram[i] ||= 0 histogram[i] += 1 end # Print the histogram pp histogram puts histogram.each { | v | puts "*" * v }
-
Hmm. You're right, Owen. I guess nobody else had generated random data with numbers in the max bucket. Sorry about that! -- Scott
-
I've fixed the bug Owen pointed out in the posted code.
-
Here's one of the hash-based solutions. I've never liked the redundancy of doing "foo = Foo.new; foo.do_something; return foo" -- this would look even prettier with ActiveSupport's Object#returning. But I couldn't get it to include properly, so this will do. (=
#!/usr/bin/env ruby require 'pp' # Get data from a file, turn it in to a float, and find the max module Enumerable def value_counts h = Hash.new(0) each { |e| h[e] += 1 } h end end lines = File.readlines('random.txt') histogram = lines.map { |line| line.chomp.to_i }.value_counts # Print the histogram pp histogram puts histogram.keys.sort.each { |n| puts '*' * histogram[n] }
