How to create custom iterator for Range - ruby

I'd like to create a subclass of Range in order to specify a step size other than 1 so I can do things like:
>> a = RangeWithStepSize.new(-1, 2, 0.5).each {|x| puts(x)}
-1.0
-0.5
0.0
0.5
1.0
1.5
2.0
=> -1..2
My first attempt at an implementation doesn't work:
class RangeWithStepSize < Range
attr_reader :step_size
def initialize(start_v, end_v, step_size, exclusive = false)
super(start_v, end_v, exclusive)
#step_size = step_size
end
def each
self.step(step_size).each
end
end
>> a = RangeWithStepSize.new(-1, 2, 0.5).each {|x| puts(x)}
=> #<Enumerator: [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0]:each>
It appears that RangeWithStepSize#each is returning a valid enumerator, but it doesn't enumerate. Any idea why?
<aside>This may be related, but I notice is that Range#step without a block does NOT return an enumerator as specified in the documentation; it returns an array instead:
>> Range.new(-1, 2).step(0.5).class
=> Array
An Array is enumerable, but it is not an Enumerator. Is this a documentation bug?</aside>
clarification
I'd like to make a version of Range that encapsulates a step size, so I can do:
a = RangeWithStepSize(-1, 2, 0.5)
b = RangeWithStepSize(-1, 2, 0.25)
... so enumerating on a produces a step size of 0.5 and b produces 0.25.

You know you can do this, right? Inheritance isn't necessary here.
(-1..2).step(0.5) do |x|
puts x
end
Your code will work with a few small adjustments:
class RangeWithStepSize < Range
attr_reader :step_size
def initialize(start_v, end_v, step_size, exclusive = false)
super(start_v, end_v, exclusive)
#step_size = step_size
end
def each (&block)
self.step(step_size).each(&block)
end
end

Related

Ruby Code-Cutting (few lines as possible)

So the code below is what I'm trying to cut down to as few lines as possible. Any other ruby tricks out there I could use to shorten it? I appreciate all help anyone can offer.
Article Class:
class Article
attr_accessor :id, :price, :quantity
def initialize(id, price, quantity)
#id, #price, #quantity = id, Float(price), quantity.to_i
end
end
Order Class:
class Order
def initialize(name)
#a, i = [], 0
input = File.open(name, "r")
while(id = input.gets.chomp)
j, price = 0, input.gets.chomp
while(j<#a.length)
if(#a[j].id.eql?(id.to_i))
#a[j].quantity += 1
end
end
else
#a[i] = new Article(id,price,1)
i+=1
end
end
end
def orderCost
sum = 0
#a.each { |e| sum+=(e.price * e.quantity)}
sum = ((sum*1.07) + 2.99)
end
def displaySelectArticles
min, max = #a[0], #a[0]
#a.each do |e|
if(min.cost > e.cost)
min = e
end
if(max.cost < e.cost)
max = e
end
sum += e.cost*e.quantity and q += e.quantity
end
puts "Min: #{min.cost} | Max: #{max.cost} | Avg: #{Float(sum)/q}"
end
end
I did my best here but your initialize method didnt make any logical sense to me. Hopefully this can at least guide you into the right direction. Disclaimer: None of this was tested and I wrote it off of what methods I could remember.
class Order
def initialize(name)
#a, i = [], 0
File.readlines(name) do |line|
# This while loop makes no sense to me
# Seems like a surefire infiniteloop because if id = 3, it is always true
# Maybe you meant to do an operator like == for comparison
while(id = line)
j, price = 0, line
while j < #a.length
#a[j].quantity += 1 if(#a[j].id.eql?(id.to_i))
end
else
#a[i] = new Article(id, price, 1)
i += 1
end
end
end
def orderCost
# I would probably make this two lines because its unreadable
(#a.inject(0) { |accum, e| accum + (e.price * e.quantity) } * 1.07) + 2.99
end
def displaySelectArticles
min, max = #a[0], #a[0]
#a.each do |e|
min = e if min.cost > e.cost
max = e if max.cost < e.cost
sum += e.cost * e.quantity
q += e.quantity # I have no idea how you got a q
end
puts "Min: #{min.cost} | Max: #{max.cost} | Avg: #{Float(sum)/q}"
end
end
The Article class needs serious attention because of that jumble of junk where
three variables are assigned at the same time. Split that out into three very
simple assignments:
class Article
attr_accessor :id, :price, :quantity
def initialize(id, price, quantity)
#id = id
#price = price.to_f
#quantity = quantity.to_i
end
end
Using x.to_f is preferable to Float(x), it's a gentler approach to
converting. If you're doing financial calculations I'd stronly encourage
you to use something like BigDecimal
as floating point numbers are notoriously problematic. $1.99 has a way of
becoming $1.989999423489 which when rounded can appear to "lose" a cent here
or there. The BigDecimal class avoids this by representing values as exact.
The rest is basically attacking the problem of not using Enumerable
effectively and instead writing your own routines to do what should be simple:
class Order
def initialize(name)
#items = [ ]
File.open(name, "r") do |input|
while(id = input.gets.chomp)
price = input.gets.chomp
# find locates the first matching thing in the array, if any.
existing = #items.find do |item|
item.id == id
end
if (existing)
existing.quantity += 1
else
items << Article.new(id, price, 1)
end
end
end
def item_cost
# Inject is good at iterating and accumulating
#items.inject(0.0) do |sum, item|
sum + item.price * item.quantity
end
end
def item_count
#items.inject(0) do |sum, item|
sum + item.quantity
end
end
def order_cost
item_cost * 1.07 + 2.99
end
def display_select_articles
# minmax_by finds min and max entries based on arbitrary criteria
min, max = #items.minmax_by { |item| item.price }
sum = item_cost
count = item_count
puts "Min: %f | Max: %f | Avg: %f" % [
min ? min.cost : 0.0,
max ? max.cost : 0.0,
count > 0.0 ? (sum / count) : 0.0
]
end
end
Whenever possible go with the grain, and that means using Ruby's structures and their native methods. The more you deviate from this the more code you'll have to write, and the uglier your code will get. Sometimes you're solving a difficult problem and you have no choice, but this example is not one of those cases. Everything here is simpler if you just do it the Ruby way.

Koans score_project and understanding the method needed

I am currently working through the Ruby Koans and I am stuck on the scoring project. First of all, I am having a hard time evaluating the instructions and laying it out in terms of what I am suppose to do. Secondly, I am not sure if I am on the right track with the method score I wrote below. My questions is - Is there a way to understand these instructions better? Also, with the score method I wrote, I am still not passing the first test. I think I must understand what I need to do first but I can't figure it out. Any help and simple explanations or direction is appreciated.
Thank you.
Greed is a dice game where you roll up to five dice to accumulate
points. The following "score" function will be used to calculate the
score of a single roll of the dice.
A greed roll is scored as follows:
A set of three ones is 1000 points
A set of three numbers (other than ones) is worth 100 times the
number. (e.g. three fives is 500 points).
A one (that is not part of a set of three) is worth 100 points.
A five (that is not part of a set of three) is worth 50 points.
Everything else is worth 0 points.
Examples:
score([1,1,1,5,1]) => 1150 points
score([2,3,4,6,2]) => 0 points
score([3,4,5,3,3]) => 350 points
score([1,5,1,2,4]) => 250 points
More scoring examples are given in the tests below:
Your goal is to write the score method.
def score(dice)
(1..6).each do |num|
amount = dice.count(num)
if amount >= 3
100 * num
elsif num == 1
100 * amount
elsif num == 5
50 * amount
else
0
end
end
end
# test code for method
class AboutScoringProject < Neo::Koan
def test_score_of_an_empty_list_is_zero
assert_equal 0, score([])
end
def test_score_of_a_single_roll_of_5_is_50
assert_equal 50, score([5])
end
def test_score_of_a_single_roll_of_1_is_100
assert_equal 100, score([1])
end
def test_score_of_multiple_1s_and_5s_is_the_sum_of_individual_scores
assert_equal 300, score([1,5,5,1])
end
def test_score_of_single_2s_3s_4s_and_6s_are_zero
assert_equal 0, score([2,3,4,6])
end
def test_score_of_a_triple_1_is_1000
assert_equal 1000, score([1,1,1])
end
def test_score_of_other_triples_is_100x
assert_equal 200, score([2,2,2])
assert_equal 300, score([3,3,3])
assert_equal 400, score([4,4,4])
assert_equal 500, score([5,5,5])
assert_equal 600, score([6,6,6])
end
def test_score_of_mixed_is_sum
assert_equal 250, score([2,5,2,2,3])
assert_equal 550, score([5,5,5,5])
assert_equal 1100, score([1,1,1,1])
assert_equal 1200, score([1,1,1,1,1])
assert_equal 1150, score([1,1,1,5,1])
end
end
This is what I have done:
def score(dice)
score = 0
return score if dice == nil || dice == []
quantity = dice.inject(Hash.new(0)) {|result,element| result[element] +=1; result}
score += quantity[1] >= 3 ? 1000 + ((quantity[1] - 3) * 100) : quantity[1] * 100
score += quantity[5] >= 3 ? 500 + ((quantity[5] - 3) * 50) : quantity[5] * 50
[2,3,4,6].each {|x| score += x * 100 if quantity[x] >= 3 }
score
end
You can use hash for score instead of writing switch-case
def score(dice)
score_map = {
1 => 100,
5 => 50
}
cluster = dice.inject(Hash.new(0)) {|hash, number| hash[number] += 1; hash}
cluster.inject(0) do |sum, (num, count)|
set_count = count / 3
sum += num == 1 ? 1000 * set_count : num * 100 * set_count
sum + (score_map[num] || 0) * (count % 3) # use 0 if score of num dosn't exist
end
end

Cumulative weighted average in ruby

I am trying to implement a cumulative weighted average function that takes as argument the
list
[[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]]
and returns (rounded to 2 decimal places here)
[3.1, 2.47, 4.08, 5.81]
For, example: 2.47 = (1000 * 3.1 + 500 * 1.2) / 1500.
I have currently solved this using the following piece of code:
def cumulative_weighted_average(list)
cs = 0
qu = 0
res = list.inject([0]) do |s, el|
cs += el[0] * el[1]
qu += el[0]
s + [cs.to_f / qu]
end
res.shift
res
end
Is there a shorter (more compact) way of doing this?
Edit:
Thanks for the answers below! The list will on average contain about 1000 entries, so not sure about the speed requirement. Since I need to be able to essentially track two values within the block, is there some extension of inject that allows you to write
list.inject([0,0]){ |s1, s2, el| ...}
where s1 and s2 are initialized to 0?
I think this is what you want:
def cumulative_weighted_average list
cs, qu = 0.0, 0.0
list
.map{|x, w| [cs += x * w, qu += x]}
.map{|cs, qu| cs / qu}
end
cumulative_weighted_average([[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]])
# => [3.1, 2.466666666666667, 4.078260869565217, 5.812222222222222]
For the additional question, things like this are possible:
list.inject([0,0]){|(s1, s2), el| ...}
Is there a shorted (more compact) way of doing this?
I can try for you..
arr = [[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]]
arr2 = (1..arr.size).map do |i|
b = arr.take(i)
b.reduce(0){|sum,a| sum + a.reduce(:*)}/b.reduce(0){|sum,k| sum + k[0]}
end
arr2
# => [3.1, 2.466666666666667, 4.078260869565217, 5.812222222222222]
You can avoid the "outer" temporary variables, and make things look a little cleaner, and idiomatic Ruby, if you allow for a two-stage calculation (which is not necessarily slower, same amount of maths is involved):
def cumulative_weighted_average list
cumulative_totals = list.inject( [] ) do |cumulative,item|
tot_count, tot_sum = cumulative.last || [0, 0.0]
next_count, next_value = item
cumulative << [ tot_count + next_count, tot_sum + next_count * next_value ]
end
cumulative_totals.map { |count,sum| sum/count }
end
p cumulative_weighted_average(
[[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]] )
=> [3.1, 2.46666666666667, 4.07826086956522, 5.81222222222222]

Calculating Median in Ruby

How do I calculate the median of an array of numbers using Ruby?
I am a beginner and am struggling with handling the cases of the array being of odd and even length.
Here is a solution that works on both even and odd length array and won't alter the array:
def median(array)
return nil if array.empty?
sorted = array.sort
len = sorted.length
(sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0
end
Similar to nbarraille's, but I find it a bit easier to keep track of why this one works:
class Array
def median
sorted = self.sort
half_len = (sorted.length / 2.0).ceil
(sorted[half_len-1] + sorted[-half_len]) / 2.0
end
end
half_len = number of elements up to and including (for array with odd number of items) middle of array.
Even simpler:
class Array
def median
sorted = self.sort
mid = (sorted.length - 1) / 2.0
(sorted[mid.floor] + sorted[mid.ceil]) / 2.0
end
end
If by calculating Median you mean this
Then
a = [12,3,4,5,123,4,5,6,66]
a.sort!
elements = a.count
center = elements/2
elements.even? ? (a[center] + a[center+1])/2 : a[center]
def median(array) #Define your method accepting an array as an argument.
array = array.sort #sort the array from least to greatest
if array.length.odd? #is the length of the array odd?
array[(array.length - 1) / 2] #find value at this index
else array.length.even? #is the length of the array even?
(array[array.length/2] + array[array.length/2 - 1])/2.to_f
#average the values found at these two indexes and convert to float
end
end
More correct solution with handling edge cases:
class Array
def median
sorted = self.sort
size = sorted.size
center = size / 2
if size == 0
nil
elsif size.even?
(sorted[center - 1] + sorted[center]) / 2.0
else
sorted[center]
end
end
end
There is a specs to prove:
describe Array do
describe '#median' do
subject { arr.median }
context 'on empty array' do
let(:arr) { [] }
it { is_expected.to eq nil }
end
context 'on 1-element array' do
let(:arr) { [5] }
it { is_expected.to eq 5 }
end
context 'on 2-elements array' do
let(:arr) { [1, 2] }
it { is_expected.to eq 1.5 }
end
context 'on odd-size array' do
let(:arr) { [100, 5, 2, 12, 1] }
it { is_expected.to eq 5 }
end
context 'on even-size array' do
let(:arr) { [7, 100, 5, 2, 12, 1] }
it { is_expected.to eq 6 }
end
end
end
I like to use Refinements, which is a safe way to Monkey Patch the ruby classes without collateral effects over the system.
The usage become much more cleaner than a new method.
With the Refinements you can monkey patch the Array class, implement the Array#median and this method will only be available inside the scope of the class that is using the refinement! :)
Refinements
module ArrayRefinements
refine Array do
def median
return nil if empty?
sorted = sort
(sorted[(length - 1) / 2] + sorted[length / 2]) / 2.0
end
end
end
class MyClass
using ArrayRefinements
# You can use the Array#median as you wish here
def test(array)
array.median
end
end
MyClass.new.test([1, 2, 2, 2, 3])
=> 2.0
def median(array)
half = array.sort!.length / 2
array.length.odd? ? array[half] : (array[half] + array[half - 1]) / 2
end
*If the length is even, you must add the middle point plus the middle point - 1 to account for the index starting at 0
def median(arr)
sorted = arr.sort
if sorted == []
return nil
end
if sorted.length % 2 != 0
result = sorted.length / 2 # 7/2 = 3.5 (rounded to 3)
return sorted[result] # 6
end
if sorted.length % 2 == 0
result = (sorted.length / 2) - 1
return (sorted[result] + sorted[result+1]) / 2.0 # (4 + 5) / 2
end
end
p median([5, 0, 2, 6, 11, 10, 9])
Here's a solution:
app_arry = [2, 3, 4, 2, 5, 6, 16].sort
# check array isn't empty
if app_arry.empty? || app_arry == ""
puts "Sorry, This will not work."
return nil
end
length = app_arry.length
puts "Array length = #{length}"
puts "Array = #{app_arry}"
if length % 2 == 0
# even number of elements
puts "median is #{(app_arry[length/2].to_f + app_arry[(length-1)/2].to_f)/2}"
else
# odd number of elements
puts "median is #{app_arry[(length-1)/2]}"
end
OUTPUT
Array length = 7
Array = [2, 3, 4, 2, 5, 6, 16]
median is 2
def median(array, already_sorted=false)
return nil if array.empty?
array = array.sort unless already_sorted
m_pos = array.size / 2
return array.size % 2 == 1 ? array[m_pos] : mean(array[m_pos-1..m_pos])
end
There are many ways to do this, but for both performance and reliability, I suggest using the enumerable-statistics library created by Ruby committer mrkn.
https://github.com/mrkn/enumerable-statistics
require 'enumerable/statistics'
ary = [1,2,3,3,4]
ary.mean # => 2.6
ary.median # => 3
I think it's good:
#!/usr/bin/env ruby
#in-the-middle value when odd or
#first of second half when even.
def median(ary)
middle = ary.size/2
sorted = ary.sort_by{ |a| a }
sorted[middle]
end
or
#in-the-middle value when odd or
#average of 2 middle when even.
def median(ary)
middle = ary.size/2
sorted = ary.sort_by{ |a| a }
ary.size.odd? ? sorted[middle] : (sorted[middle]+sorted[middle-1])/2.0
end
I used sort_by rather than sort because it's faster: Sorting an array in descending order in Ruby.

How can I do standard deviation in Ruby?

I have several records with a given attribute, and I want to find the standard deviation.
How do I do that?
module Enumerable
def sum
self.inject(0){|accum, i| accum + i }
end
def mean
self.sum/self.length.to_f
end
def sample_variance
m = self.mean
sum = self.inject(0){|accum, i| accum +(i-m)**2 }
sum/(self.length - 1).to_f
end
def standard_deviation
Math.sqrt(self.sample_variance)
end
end
Testing it:
a = [ 20, 23, 23, 24, 25, 22, 12, 21, 29 ]
a.standard_deviation
# => 4.594682917363407
01/17/2012:
fixing "sample_variance" thanks to Dave Sag
It appears that Angela may have been wanting an existing library. After playing with statsample, array-statisics, and a few others, I'd recommend the descriptive_statistics gem if you're trying to avoid reinventing the wheel.
gem install descriptive_statistics
$ irb
1.9.2 :001 > require 'descriptive_statistics'
=> true
1.9.2 :002 > samples = [1, 2, 2.2, 2.3, 4, 5]
=> [1, 2, 2.2, 2.3, 4, 5]
1.9.2p290 :003 > samples.sum
=> 16.5
1.9.2 :004 > samples.mean
=> 2.75
1.9.2 :005 > samples.variance
=> 1.7924999999999998
1.9.2 :006 > samples.standard_deviation
=> 1.3388427838995882
I can't speak to its statistical correctness, or your comfort with monkey-patching Enumerable; but it's easy to use and easy to contribute to.
The answer given above is elegant but has a slight error in it. Not being a stats head myself I sat up and read in detail a number of websites and found this one gave the most comprehensible explanation of how to derive a standard deviation. http://sonia.hubpages.com/hub/stddev
The error in the answer above is in the sample_variance method.
Here is my corrected version, along with a simple unit test that shows it works.
in ./lib/enumerable/standard_deviation.rb
#!usr/bin/ruby
module Enumerable
def sum
return self.inject(0){|accum, i| accum + i }
end
def mean
return self.sum / self.length.to_f
end
def sample_variance
m = self.mean
sum = self.inject(0){|accum, i| accum + (i - m) ** 2 }
return sum / (self.length - 1).to_f
end
def standard_deviation
return Math.sqrt(self.sample_variance)
end
end
in ./test using numbers derived from a simple spreadsheet.
#!usr/bin/ruby
require 'enumerable/standard_deviation'
class StandardDeviationTest < Test::Unit::TestCase
THE_NUMBERS = [1, 2, 2.2, 2.3, 4, 5]
def test_sum
expected = 16.5
result = THE_NUMBERS.sum
assert result == expected, "expected #{expected} but got #{result}"
end
def test_mean
expected = 2.75
result = THE_NUMBERS.mean
assert result == expected, "expected #{expected} but got #{result}"
end
def test_sample_variance
expected = 2.151
result = THE_NUMBERS.sample_variance
assert result == expected, "expected #{expected} but got #{result}"
end
def test_standard_deviation
expected = 1.4666287874
result = THE_NUMBERS.standard_deviation
assert result.round(10) == expected, "expected #{expected} but got #{result}"
end
end
I'm not a big fan of adding methods to Enumerable since there could be unwanted side effects. It also gives methods really specific to an array of numbers to any class inheriting from Enumerable, which doesn't make sense in most cases.
While this is fine for tests, scripts or small apps, it's risky for larger applications, so here's an alternative based on #tolitius' answer which was already perfect. This is more for reference than anything else:
module MyApp::Maths
def self.sum(a)
a.inject(0){ |accum, i| accum + i }
end
def self.mean(a)
sum(a) / a.length.to_f
end
def self.sample_variance(a)
m = mean(a)
sum = a.inject(0){ |accum, i| accum + (i - m) ** 2 }
sum / (a.length - 1).to_f
end
def self.standard_deviation(a)
Math.sqrt(sample_variance(a))
end
end
And then you use it as such:
2.0.0p353 > MyApp::Maths.standard_deviation([1,2,3,4,5])
=> 1.5811388300841898
2.0.0p353 :007 > a = [ 20, 23, 23, 24, 25, 22, 12, 21, 29 ]
=> [20, 23, 23, 24, 25, 22, 12, 21, 29]
2.0.0p353 :008 > MyApp::Maths.standard_deviation(a)
=> 4.594682917363407
2.0.0p353 :043 > MyApp::Maths.standard_deviation([1,2,2.2,2.3,4,5])
=> 1.466628787389638
The behavior is the same, but it avoids the overheads and risks of adding methods to Enumerable.
The presented computation are not very efficient because they require several (at least two, but often three because you usually want to present average in addition to std-dev) passes through the array.
I know Ruby is not the place to look for efficiency, but here is my implementation that computes average and standard deviation with a single pass over the list values:
module Enumerable
def avg_stddev
return nil unless count > 0
return [ first, 0 ] if count == 1
sx = sx2 = 0
each do |x|
sx2 += x**2
sx += x
end
[
sx.to_f / count,
Math.sqrt( # http://wijmo.com/docs/spreadjs/STDEV.html
(sx2 - sx**2.0/count)
/
(count - 1)
)
]
end
end
As a simple function, given a list of numbers:
def standard_deviation(list)
mean = list.inject(:+) / list.length.to_f
var_sum = list.map{|n| (n-mean)**2}.inject(:+).to_f
sample_variance = var_sum / (list.length - 1)
Math.sqrt(sample_variance)
end
If the records at hand are of type Integer or Rational, you may want to compute the variance using Rational instead of Float to avoid errors introduced by rounding.
For example:
def variance(list)
mean = list.reduce(:+)/list.length.to_r
sum_of_squared_differences = list.map { |i| (i - mean)**2 }.reduce(:+)
sum_of_squared_differences/list.length
end
(It would be prudent to add special-case handling for empty lists and other edge cases.)
Then the square root can be defined as:
def std_dev(list)
Math.sqrt(variance(list))
end
In case people are using postgres ... it provides aggregate functions for stddev_pop and stddev_samp - postgresql aggregate functions
stddev (equiv of stddev_samp) available since at least postgres 7.1, since 8.2 both samp and pop are provided.
Or how about:
class Stats
def initialize( a )
#avg = a.count > 0 ? a.sum / a.count.to_f : 0.0
#stdev = a.count > 0 ? ( a.reduce(0){ |sum, v| sum + (#avg - v) ** 2 } / a.count ) ** 0.5 : 0.0
end
end
You can place this as helper method and assess it everywhere.
def calc_standard_deviation(arr)
mean = arr.sum(0.0) / arr.size
sum = arr.sum(0.0) { |element| (element - mean) ** 2 }
variance = sum / (arr.size - 1)
standard_deviation = Math.sqrt(variance)
end

Resources