This one was quite interesting, this isn’t the actual version I turned in.
I was sent a unit test and was asked to generate the class for it, I actually created a recursive and non-recursive solution the second time around. The first time I pretty much blew it, I should have taken a break and not have done it right after a code fest of PHP.
I am pretty proud of myself, I think ruby is pretty awesome just look I figured out how to solve this problem in 3 different ways, in a span of 5-6 hours total.
I think my final solution is way prettier, I extended the Array class and added a module. Just look at the method call in def partition(size), pretty dayam clean.
module Summable
def sum
inject {|v,n| v+n}
end
end
class Array
include Summable
def partition(size, result = [])
chunks = (self.size / size.to_f).ceil
# this will give you a delta of 1 for subset size due to recursion
# still passes test
if size == 1
result << self
else
a = Array.new(chunks)
b = self.slice!(0,chunks)
result << a[0, b.size] = b
partition(size - 1, result)
end
# this will always give you subsets equal to each other
# 1.upto(size) do |i|
# a = Array.new(chunks)
# b = self.slice!(0,chunks)
# result << a[0, b.size] = b
# end
return result
end
end
class YPSet
# init class variables
def initialize(*args)
@collection = []
args.each {|arg| @collection << arg.to_i}
@collection_size = @collection.size
end
# returns [float] the average from the set
def average
result = 0.0
sum = @collection.sum.to_f
result = sum / @collection_size
return result
end
# returns [float] the median from the set
def median
@collection.sort!
result = 0.0
mid = @collection_size / 2
# check to see if set has even or odd number of items
case is_even(@collection_size)
when true # is even
result = (@collection[mid] + @collection[mid - 1]) / 2.0
else # is odd
result = @collection[mid]
end
return result
end
def partition(size)
return @collection.partition(size)
end
# all for #4
def odd_set
result = []
0.upto(@collection_size-1) do |i|
if !is_even(@collection[i])
result << @collection[i]
end
end
return result
end
def even_set
result = []
0.upto(@collection_size-1) do |i|
if is_even(@collection[i])
result << @collection[i]
end
end
return result
end
def how_many_even
return even_set().size
end
def how_many_odd
return odd_set().size
end
#converts the entire sets items from int to bin
def convert_set_to_binary
result = []
0.upto(@collection_size-1) do |i|
result << convert_to_binary(@collection[i])
end
return result
end
# convert number to binary
def convert_to_binary(number)
number = number.to_i
result = ""
if (number == 0)
return 0
end
while (number != 0)
result = (number % 2).to_s + result
number = number / 2
end
return result
end
# is number even
def is_even(number)
result = number.to_i % 2 == 0 ? true : false
return result
end
def range()
first = @collection[0]
last = @collection[@collection_size - 1]
return (last - first)
end
end
Here’s my unit test
require 'test/unit'
require 'ypset'
class YPSetTest < Test::Unit::TestCase
def setup
end
def test_should_calculate_average
assert_equal 6, YPSet.new(8, 10, 2, 6, 4).average
assert_equal 5.5, YPSet.new(10, 11, 0, 1).average
end
# The median is the middle value in a set of values. If there are
# an even number of values, it should be the average of the two middle
# values
def test_should_calculate_median
assert_equal 6, YPSet.new(8, 10, 2, 6, 4).median
assert_equal 4.5, YPSet.new(10, 0, 1, 9, 8, 1).median
end
# for #3 you need to test for strings
def test_should_calculate_median_strings
assert_equal 6, YPSet.new("8", "10", 2, "6", 4).median
assert_equal 4.5, YPSet.new(10, "0", "1", "9", "8", "1").median
end
def also_if_nil_set
assert_equal 0, YPSet.new().average
assert_equal 0, YPSet.new().median
end
def test_should_split_into_evenly_sized_subsets
values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
subsets = YPSet.new(*values).partition(3)
assert_equal 3, subsets.length
subsets.each do |subset|
assert_equal 5, subset.length
end
assert_equal values.length, subsets.flatten.length
values << 16
subsets = YPSet.new(*values).partition(3)
assert_equal 3, subsets.length
subsets.each do |subset|
assert_in_delta 5, subset.length, 1
end
assert_equal values.length, subsets.flatten.length
values = Array.new(18923) {|i| i}
subsets = YPSet.new(*values).partition(124)
assert_equal 124, subsets.length
subsets.each do |subset|
assert_in_delta 152, subset.length, 1
end
assert_equal values.length, subsets.flatten.length
end
def test_range
values = Array.new(18923234234234234234234234) {|i| i}
assert_raise(RuntimeError) { YPSet.new(*values)}
end
# for #4
def test_is_even
assert_equal true, YPSet.new().is_even(2)
assert_equal false, YPSet.new().is_even(1)
assert_equal true, YPSet.new().is_even("2")
assert_equal false, YPSet.new().is_even("1")
end
def test_even_set
assert_equal [2,4,6], YPSet.new(2,3,4,5,6,7).even_set
assert_equal [], YPSet.new(1,3,5,7,9,11).even_set
end
def test_odd_set
assert_equal [1,3,5,7,9], YPSet.new(1,2,3,4,5,6,7,8,9).odd_set
assert_equal [1], YPSet.new(1,2,4,6,8).odd_set
end
def test_how_many_odd
assert_equal 5, YPSet.new(1,2,3,4,5,6,7,8,9).how_many_odd
end
def test_how_many_even
assert_equal 4, YPSet.new(1,2,3,4,5,6,7,8,9).how_many_even
assert_equal 0, YPSet.new(1,3,5,7,9).how_many_even
#strings
assert_equal 0, YPSet.new(1,3,"5","7","9").how_many_even
end
def test_convert_to_binary
assert_equal "101", YPSet.new().convert_to_binary("5")
assert_equal "11", YPSet.new().convert_to_binary(3)
assert_equal "10", YPSet.new().convert_to_binary(2)
end
def test_convert_set_to_binary
assert_equal ["1","10","11","100","101"], YPSet.new(1,2,3,4,5).convert_set_to_binary
end
def test_range
assert_equal 9, YPSet.new(1,10).range
end
end
for the partition part here is my first soltuion: See the instants variable @ and ; this was due to me spending all day before taking the test working in PHP, ugly isn’t it? I kept looking at the code thinking “This looks like PHP not Ruby!”, there had to be a better solution to this problem, a Ruby way! So I actually took a breath and read some Ruby Docs on Arrays and Sets and came on the above solution.
def partition(size)
@length = @numbers.length;
@mid = (@length / size);
@parts = [];
@start = 0;
1.upto(size) do |i|
@last = @start+@mid;
@last = @last-1 unless @length%size >= i;
@parts << @numbers[@start..@last] || [];
@start = @last+1;
end
return @parts
end