Build a kind of hash-tree from arrays - ruby

Say we have array of arrays:
tree_limbs = Array.new
tree_limbs << %w(1 2 3 4 5 7)
tree_limbs << %w(1 2 3 4 6)
tree_limbs << %w(1 2 3 8 9 10 11)
tree_limbs << %w(1 2 3 8 9 10 12)
What's the effective way to build such a hash tree within ruby:
tree_hash = {1 =>
{2 =>
{3 =>
{4 =>
{5 =>
{7 => nil}
},
{6 => nil}
},
{8 =>
{9 =>
{10 =>
{11 => nil},
{12 => nil}
}
}
}
}
}
}

If you definitely want explicit nil's on the last level then you can do something like that:
tree_limbs = Array.new
tree_limbs << %w(1 2 3 4 5 7)
tree_limbs << %w(1 2 3 4 6)
tree_limbs << %w(1 2 3 8 9 10 11)
tree_limbs << %w(1 2 3 8 9 10 12)
tree_hash = {}
tree_limbs.each do |path|
path.each_with_index.inject(tree_hash) do |node, (step, index)|
if index < path.size - 1
node[step] ||= {}
else
node[step] = nil
end
end
end
p tree_hash
#=> {"1"=>{"2"=>{"3"=>{"4"=>{"5"=>{"7"=>nil}, "6"=>nil}, "8"=>{"9"=>{"10"=>{"11"=>nil, "12"=>nil}}}}}}}

Related

2 to the power of multiples numbers

I'm trying to create a method that takes a number and then puts every number between that and 0 as an exponent to the number 2.
2^0 = 1
2^1 = 2
2^2 = 4
2^3 = 8
2^4 = 16
This is as far as I've gotten. . .
def powers_of_two(n)
n.map { |x| 2 ** (0..x) }
end
I believe I'm not far off in my attempt.
For example, like this:
def power_of_two(n) = (0..n).each { puts 2 ** _1 }
power_of_two(4)
#=> 1
2
4
8
16
You can build an enumerator that generates the numbers via Enumerator.produce:
powers = Enumerator.produce(1) { |i| i * 2 }
powers.take(5)
#=>[1, 2, 4, 8, 16]
The block generates each number based on the previous one, starting at 1.
The same works for bit-shifting:
powers = Enumerator.produce(1) { |i| i << 1 }
powers.take(5)
#=>[1, 2, 4, 8, 16]
As we are dealing with powers of 2, try this bit-shifting method. (I assume the given number n is non-negative.)
def po2(n)
(0..n).each { |i| puts 1 << i }
end
po2(7)
1
2
4
8
16
32
64
128
See Integer#<< and possibly this tutorial.
One could alternatively create a proc.
po2 = Proc.new { |n| (0..n).each { |i| puts 1 << i } }
#=> #<Proc:0x00007f91a5222bb0 <main>:0>
po2(7)
1
2
4
8
16
32
64
128

hash.merge with block to add up sub-hash keys?

I have read something about hash merge with a block and this is working fine for simple, non-nested hashes in plain ruby. The following code results in {1=>2, 2=>4, 4=>6} as expected:
a = {1 => 1, 2 => 2, 4 => 3}
b = {1 => 1, 2 => 2, 4 => 3}
a.merge(b) { |key, value_a, value_b | value_a + value_b }
But the merge is not working for a nested hash structure, I get a NoMethodError (undefined method '+' for {1=>1, 2=>2}:Hash)
a = { "2018" => {1 => 1, 2 => 2, 4 => 3} }
b = { "2019" => {1 => 1, 2 => 2, 4 => 3} }
c = a.merge(b) { |key, value_a, value_b | value_a + value_b }
I have read about each_with_object and I am unsure how to use it. Is there a smart way to accomplish the merge of the values of the sub-hash? What do you think is the easiest way?
You can use Hash#deep_merge from active support to do this.
require 'active_support/all'
a = { k1: { k2: 1 } }
b = { k1: { k2: 2 } }
a.deep_merge(b) { |k, v1, v2| v1 + v2 }
# => { l1: { k2: 3 } }
Nested Hash, nested Hash#merge?
I changed the key of b to "2018"
a = { "2018" => {1 => 1, 2 => 2, 4 => 3} }
b = { "2018" => {1 => 1, 2 => 2, 4 => 3} }
c = a.merge(b) { |k, v1, v2| v1.merge(v2) { |kk, aa, bb | aa + bb } }
#=> {"2018"=>{1=>2, 2=>4, 4=>6}}
For your original values:
a = { "2018" => {1 => 1, 2 => 2, 4 => 3} }
b = { "2019" => {1 => 1, 2 => 2, 4 => 3} }
The result is
#=> {"2018"=>{1=>1, 2=>2, 4=>3}, "2019"=>{1=>1, 2=>2, 4=>3}}

Trouble with classes in ruby and codewars

so i am trying a kata on Codewars: Flexible Card Game
http://www.codewars.com/kata/5436fdf34e3d6cb156000350/train/ruby
the code I have written has passed most tests but trips up at the end giving this:
#draw
chooses cards from the end
Test Passed: Value == [[:clubs, 13]]
removes cards from the deck
Test Passed: Value == 51
returns the cards that were drawn
Test Passed: Value == 1
Expected [:clubs, 13] to be a Card
chooses cards from the end
Test Passed: Value == [[:clubs, 12], [:clubs, 13]]
removes cards from the deck
Test Passed: Value == 50
returns the cards that were drawn
Test Passed: Value == 2
Expected [:clubs, 12] to be a Card
Expected [:clubs, 13] to be a Card
What I don't understand is that when the test calls the method draw it seems to expect to different returns from the same method. I'm sure it's something I've done wrong but I cant see it. Any help would be great. Here is my code:
class Card
include Comparable
attr_accessor :suit, :rank
def initialize(suit, rank)
#suit = suit
#rank = rank
end
def <=> (another_card)
if self.rank < another_card.rank
-1
elsif self.rank > another_card.rank
1
else
0
end
end
def face_card?
#rank > 10 ? true : false
end
def to_s
#rank_hash = {13 => "King", 12 => "Queen", 11 => "Jack", 10 => "10", 9 => "9", 8 => "8", 7 => "7", 6 => "6", 5 => "5", 4 => "4", 3 => "3", 2 => "2", 1 => "Ace"}
#suit_hash = {:clubs => "Clubs", :spades => "Spades", :hearts => "Hearts", :diamonds => "Diamonds"}
"#{#rank_hash[#rank]} of #{#suit_hash[#suit]}"
end
end
class Deck < Card
attr_accessor :cards
def initialize
#rank_array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
#suit_array = [:hearts, :diamonds, :spades, :clubs]
#cards = #suit_array.product(#rank_array)
end
def count
#cards.size
end
def shuffle
#cards.shuffle!
end
def draw(n=1)
#cards.pop(n)
end
end
Issue was with the initialize method. Below fixed the issue.
def initialize
#cards = []
#rank_array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
#suit_array = [:hearts, :diamonds, :spades, :clubs]
#suit_array.each do |suit|
#rank_array.each do |rank|
each_card = Card.new(suit, rank)
#cards << each_card
end
end
end

map_values() for Ruby hashes?

I miss a Hash method in Ruby to transform/map only the values of the hash.
h = { 1 => [9,2,3,4], 2 => [6], 3 => [5,7,1] }
h.map_values { |v| v.size }
#=> { 1 => 4, 2 => 1, 3 => 3 }
How do you archive this in Ruby?
Update: I'm looking for an implementation of map_values().
# more examples
h.map_values { |v| v.reduce(0, :+) }
#=> { 1 => 18, 2 => 6, 3 => 13 }
h.map_values(&:min)
#=> { 1 => 2, 2 => 6, 3 => 1 }
Ruby 2.4 introduced the methods Hash#transform_values and Hash#transform_values! with the desired behavoir.
h = { 1=>[9, 2, 3, 4], 2=>[6], 3=>[5, 7, 1] }
h.transform_values { |e| e.size }
#=> {1=>4, 2=>1, 3=>3}
You can monkey-patch the hash class, like this
class Hash
def map_values
map { |k, v|
[k, yield(v)]
}.to_h
end
end
p ({1 => [1,1,1,1], 2 => [2], 3 => [3,3,3]}.map_values { |e| e.size })
You can also use Hash#update for this:
h = { 1 => [9, 2, 3, 4], 2 => [6], 3 => [5, 7, 1] }
h.update(h) { |_, v| v.size }
#=> { 1 => 4, 2 => 1, 3 => 3 }
It replaces all values that have duplicate keys in one hash with that of another, or, if a block is given, with the result of calling the block. You can pass the original hash as the argument to ensure all values are replaced.
Note that this modifies the hash in place! If you want to preserve the original hash, dup it first:
h.dup.update(h) { |_, v| v.size }
#=> { 1 => 4, 2 => 1, 3 => 3 }
h
#=> { 1 => [9, 2, 3, 4], 2 => [6], 3 => [5, 7, 1] }
This will do the trick for you
h = { 1 => [1,1,1,1], 2 => [2], 3 => [3,3,3] }
h.map {|k,v| [k, v.size] }.to_h
No map, just each
h = { 1 => [1,1,1,1], 2 => [2], 3 => [3,3,3] }
h.each{|k,v| h[k] = v.size}
You can achieve this by:
h.map { |a, b| [a, b.size] }.to_h
#=> {1=>4, 2=>1, 3=>3}
Here is one more way to achieve it:
h = { 1 => [1,1,1,1], 2 => [2], 3 => [3,3,3] }
p h.keys.zip(h.values.map(&:size)).to_h
#=> {1=>4, 2=>1, 3=>3}
There's an implementation of this method in the DeepEnumerable library: https://github.com/dgopstein/deep_enumerable/
It's called shallow_map_values:
>> require 'deep_enumerable'
>> h = { 1 => [9,2,3,4], 2 => [6], 3 => [5,7,1] }
>> h.shallow_map_values { |v| v.size }
=> {1=>4, 2=>1, 3=>3}

Cycle through elements of an array

I would like to cycle #a from 0 through 2: 0, 1, 2, 0, 1, 2.
def set_a
if #a == 2
#a = 0
else
#a = #a + 1
end
end
Maybe there is a better way?
(0..2).cycle(3) { |x| puts x } #=> 0,1,2,0,1,2,0,1,2
item = [0, 1, 2].cycle.each
item.next #=> 0
item.next #=> 1
item.next #=> 2
item.next #=> 0
...

Resources