Iterator should yied error message Ruby, rspec - ruby

I'm supposed to return odd values from an array, but I keep getting failure in rspec if I pass this
odd_elements([1, 2, 3, 4, 5, 6]) { |x| x**2 }
My code looks like this
def odd_elements(array)
array.values_at(* array.each_index.select {|i| i.odd?})
end
Code in rspec is:
describe 'Odd iterator' do
context 'should yield' do
subject(:res) { odd_elements([1, 2, 3, 4, 5, 6]) { |x| x**2 } }
it { is_expected.to be_an_instance_of Array }
it { expect(res.size).to be 3 }
it { expect(res[0]).to be 4 }
it { expect(res[1]).to be 16 }
it { expect(res[2]).to be 36 }
end
end
Error I get is Odd iterator should yield should get 4 (and the same for other two numbers, 16 and 36 respectively).
Could someone please tell me, why the code in curly brackets doesn't get executed before being passed to the odd_modules?

It's up to you execute the block if odd_elements is defined by you.
def odd_elements(array)
array.map! { |item| yield item } if block_given?
array.select(&:odd?)
end
output
2.3.1 :088 > odd_elements([1,2,3,4])
=> [1, 3]
2.3.1 :088 > odd_elements([1,2,3,4]) { |x| x + 1 }
=> [3, 5]

Related

Turning several Enumerables into one

Is there a way to get several Enumerable objects to appear as a single Enumerable without flattening it into an Array? Currently I've written a class like so, but I feel there must be a built-in solution.
class Enumerables
include Enumerable
def initialize
#enums = []
end
def <<(enum)
#enums << enum
end
def each(&block)
if block_given?
#enums.each { |enum|
puts "Enumerating #{enum}"
enum.each(&block)
}
else
to_enum(:each)
end
end
end
enums = Enumerables.new
enums << 1.upto(3)
enums << 5.upto(8)
enums.each { |s| puts s }
As a simple example, it needs to be able to accept an infinite enumerator like so.
inf = Enumerator.new { |y| a = 1; loop { y << a; a +=1 } };
Well, it might be done with standard library using Enumerator. The advantage of this approach would be it returns the real enumerator, that might be mapped, reduced etc.
MULTI_ENUM = lambda do |*input|
# dup is needed here to prevent
# a mutation of inputs when given
# as a splatted param
# (due to `input.shift` below)
input = input.dup.map(&:to_enum)
Enumerator.new do |yielder|
loop do
# check if the `next` is presented
# and mutate the input swiping out
# the first (already iterated) elem
input.first.peek rescue input.shift
# stop iteration if there is no input left
raise StopIteration if input.empty?
# extract the next element from
# the currently iterated enum and
# append it to our new Enumerator
yielder << input.first.next
end
end
end
MULTI_ENUM.(1..3, 4.upto(5), [6, 7]).
map { |e| e ** 2 }
#⇒ [1, 4, 9, 16, 25, 36, 49]
After all. Use Enumerable::Lazy#flat_map with .each.lazy on elements:
inf = Enumerator.new { |y| a = 1; loop { y << a; a += 1 } }
[(1..3).to_a, inf].lazy.flat_map { |e| e.each.lazy }.take(10).force
#⇒ [1, 2, 3, 1, 2, 3, 4, 5, 6, 7]
I ended up with this solution, maybe is close to what you already tried:
def enumerate(*enum)
enum.each_with_object([]) { |e, arr| arr << e.to_a }.flatten
end
enumerate( 1..3, 5.upto(8), 3.times, 'a'..'c' ).each { |e| p e }
# => 1, 2, 3, 5, 6, 7, 8, 0, 1, 2, "a", "b", "c"
Or (same mechanics):
def enumerate(*enum)
enum.flat_map { |e| e.to_a }
end

easier and shorter way to sum values in a hash of hashes in ruby

Having a hash of hashes like this:
d = {a: {c: 1, d:3 }, b: {c: 2, d: 6}, ...}
What is the easier way to sum all values in c:?
d.values.map { |val| val[:c] }.reduce(&:+)
To explain:
d.values
=> [{:c=>1, :d=>3}, {:c=>2, :d=>6}]
d.values.map { |val| val[:c] }
=> [1, 2]
From this point you can use reduce(&:+) to get the sum, or if you're using rails (or have required active support), you can use Array#sum
reduce(&:+), by the way, is a shorthand for reduce { |memo, val| memo + val }
Just go over the hashes and sum their :c values.
d.values.sum { |h| h[:c] }
=> 3
Even shorter (from Sagar Pandya's comment):
d.sum { |_, v| v[:c] }
=> 3
If you wish to permit nested hashes of arbitrary depth you can use the following recursive method.
def sum_cees(h)
h.sum { |k,v| v.is_a?(Hash) ? sum_cees(v) : k == :c ? v : 0 }
end
sum_cees({ a: { c: 1, d:3 }, b: { d: { m: { c: 2, e: 6 } }, f: { c: 3} },
g: { c: 4 }, n: { r: 3 } })
#=> 10

Ruby: reuse value in a block without assigning it to variable (write object method on the fly)

There are several situations where I'd like to apply a block to a certain value and use the value inside this block, to use the enumerator coding style to every element.
If such method would be called decompose, it would look like:
result = [3, 4, 7, 8].decompose{ |array| array[2] + array[3] } # result = 15
# OR
result = {:key1 => 'value', :key2 => true}.decompose{ |hash| hash[:key1] if hash[:key2] } # result = 'value'
# OR
[min, max] = [3, 4, 7, 8].decompose{ |array| [array.min, array.max] } # [min, max] = [3, 8]
# OR
result = 100.decompose{ |int| (int - 1) * (int + 1) / (int * int) } # result = 1
# OR
result = 'Paris'.decompose{ |str| str.replace('a', '') + str[0] } # result = 'PrisP'
The method simply yields self to the block, returning the block's result. I don't think it exists, but you can implement it yourself:
class Object
def decompose
yield self
end
end
[3, 4, 7, 8].decompose{ |array| array[2] + array[3] }
#=> 15
{:key1 => 'value', :key2 => true}.decompose{ |hash| hash[:key1] if hash[:key2] }
#=> "value"
[3, 4, 7, 8].decompose{ |array| [array.min, array.max] }
#=> [3, 8]
It actually exists (I could not believe it didn't).
It is called BasicObject#instance_eval. Here's the doc: http://apidock.com/ruby/BasicObject/instance_eval
Available since Ruby 1.9 as this post explains: What's the difference between Object and BasicObject in Ruby?

ruby adding range elements in each block

How can I add range elements in each block together. E.g.:
a = [[1..10, 13..20, 21..24], [34..39, 42..45], [50..55]]
outcome:
a = [[9+7+3],[5+3],[5]]
a = [20, 8, 5]
You can access the first and last elements of a range by the corresponding first and last methods. inject(:+) sums up the partial distances of all the ranges belonging to the same group.
a.map { |ran­ges| range­s.map { |rang­e| range­.last - range­.first }.inj­ect(:+) }
=> [19, 8, 5]
Or, even shorter, as suggested by tokland using Ruby 2.0:
a.map { |ran­ges| range­s.map(&:size).reduce(0, :+) }
Ruby 2.0:
a.map { |ranges| ranges.map { |r| r.size - 1 } .reduce(0, :+) }
Range class has a method called #size. Thus we can do as :
a.map { |ranges| ranges.inject(0) { |sum,rng| sum + rng.size - 1 } }

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.

Resources