Ruby - How to use Hash#to_proc? - ruby

The documentation is empty, but I want to know how to use it to know when it can be useful.

that is so you can pass a hash proc into something like map.
https://bugs.ruby-lang.org/issues/11653
my_hash = ->key{{
a: 1, b: 2, c: 3, d: 4, e: 5, f: 6
}[key]}
my_hash[:a]
# => 1
[:e, :a, :b, :f, :c, :d].map(&my_hash) # hash is now mappable
# => [5, 1, 2, 6, 3, 4]

Related

Ruby hash by reference

I want to have a single target and then have that target be modifiable in a hash.
target = [1,2,3]
hash = {1 => target, 2 => target}
Now, I want to be able to either a) change target and have the hash auto update or b) change the hash's 1 and have it automatically change hash's 2. Neither work:
target = [6,7,8]
target
output:
{1=>[1, 2, 3], 2=>[1, 2, 3]}
Plan b:
hash[1] = [6,7,8]
output:
{1=>[6, 7, 8], 2=>[1, 2, 3]}
So I'm discerning that when you make a subhash an rvalue in a hash in Ruby, it's duping the subhash before setting the lvalue to equal it. What I want is the for Ruby to not to do that. Is this possible?
Thanks,
Kevin
target is just a name, [1,2,3] is the object it is referring to. Arrays are mutable, so you can change them:
target = [1,2,3]
p hash = {1 => target, 2 => target} # =>{1=>[1, 2, 3], 2=>[1, 2, 3]}
target.replace([6,7,8])
p hash # =>{1=>[6, 7, 8], 2=>[6, 7, 8]}
You should change the array itself, as already mentioned by #steenslag.
Here is just another example which also shows it wotking on a value of the Hash. You can try other methods from the Array class.
target = [1,2,3]
h = {a: target, b: target}
h #=> {:a=>[1, 2, 3], :b=>[1, 2, 3]}
[3,4,5].each do |e|
target.shift
target.push(e)
end
h #=> {:a=>[3, 4, 5], :b=>[3, 4, 5]}
[7,8,9].each do |e|
h[:a].shift
h[:a].push(e)
end
h #=> {:a=>[7, 8, 9], :b=>[7, 8, 9]}

Actual Hash getting modified when copy is modified in ruby

I am trying to copy a Hash, and then later modifying the copy of hash. But when I compare the copy with the original one, even the original hash value is getting modified.
I have tried using this:
def deep_copy(o)
Marshal.load(Marshal.dump(o))
end
h1 = {:a => 'foo'}
h2 = deep_copy(h1)
h1[:a] << 'bar'
p h2
I have also tried doing this:
def dumpable_hash(h)
return h unless h.default_proc
copy = h.clone
copy.default = nil # clear the default_proc
copy
end
Hash object(which I want to copy and keep its original unmodified):
#original = {0=>{0=>[0, 4, 5, 6], 2=>[3, 7], 1=>[1, 2]}, 1=>{0=>[0, 4, 5, 6], 2=>[1], 1=>[2, 3, 7]}, 2=>{0=>[0, 4, 6], 1=>[1, 2, 5], 2=>[3, 7]}, 3=>{0=>[0, 4], 2=>[1, 2, 3, 6, 7], 1=>[5]}, 4=>{0=>[4], 2=>[1, 5], 1=>[2, 3, 6, 7, 0]}, 5=>{1=>[0, 1, 2, 5], 2=>[3, 6, 7], 0=>[4]}, 6=>{1=>[0, 1, 2, 5, 4], 2=>[3, 6, 7], 0=>[]}}
Tried copying the original into another object, using the given answer also.
Method used for updating its clone,
#outer loop
(1..5).each do |i|
#assigning original to another object in every loop
copy = #original.clone
(-6..0).each do |row|
if copy[row.abs][0].include? k
copy[row.abs][0] -= [k]
copy[row.abs][1] += [k]
puts "row #{row.abs}, col #{k}"
break
end
end
end
When the loop is over both the original and copy are updated.
Please help, I have been trying this from an hour now.
If want to copy one hash to another you can do it just like this. Then you can manipulate the copied hash or even do it in the loop. And then manipulate the copied hash it for your task. In here it copies the key-value pair for the hash,
#original = {0=>{0=>[0, 4, 5, 6], 2=>[3, 7], 1=>[1, 2]}, 1=>{0=>[0, 4, 5, 6], 2=>[1], 1=>[2, 3, 7]}, 2=>{0=>[0, 4, 6], 1=>[1, 2, 5], 2=>[3, 7]}, 3=>{0=>[0, 4], 2=>[1, 2, 3, 6, 7], 1=>[5]}, 4=>{0=>[4], 2=>[1, 5], 1=>[2, 3, 6, 7, 0]}, 5=>{1=>[0, 1, 2, 5], 2=>[3, 6, 7], 0=>[4]}, 6=>{1=>[0, 1, 2, 5, 4], 2=>[3, 6, 7], 0=>[]}}
copy = Hash.new
#original.each do |k, v|
copy[k] = v.dup
end
p copy #prints the copied hash
I think you need to do deep_dup here to completely separate one hash content from another.
h1 = {a: "foo"}
h2 = h1.deep_dup
h2[:a] << "bar"
puts h2 #returns {:a => "foobar"}
puts h1 # returns {:a => "foo"}
Use dup.
h1 = {a:1, b:2}
h2 = h1.dup
h2[:c] = 3
puts h1
{:a=>1, :b=>2}
puts h2
{:a=>1, :b=>2, :c=>3}
If you have a nested hash, you can use ActiveSupport deep_dup.
def deep_dup
each_with_object(dup) do |(key, value), hash|
hash[key.deep_dup] = value.deep_dup
end
end
You are modifying the original hash (Appending to h1 hash). Modify the deep copied one and you can see the original is staying as before,
def deep_copy(o)
Marshal.load(Marshal.dump(o))
end
h1 = {:a => 'foo'}
h2 = deep_copy(h1)
h2[:a] << 'bar'
p h2 #prints the cloned one
p h1 #prints the original one
See this for futher information about marshaling library here.

Get key/value pairs as arrays

I've been struggling with this: Complete the keysAndValues function so that it takes in an object and returns the keys and values as separate arrays.
I've added several versions so you can see how i'm thinking through the problem.
def keysAndValues(data)
data.each do |data|
data.split(key.to_a, value.to_a)
end
data
end
keysAndValues ({a: 1, b: 2, c: 3})
def keysAndValues(data)
data.each do |data|
data.split([key], [value])
end
data
end
keysAndValues ({a: 1, b: 2, c: 3})
def keysAndValues(data)
data.each do |data|
data.slice([key], [value])
end
data
end
keysAndValues ({a: 1, b: 2, c: 3})
def keysAndValues(data)
data.each do |data|
data.slice_to.a(2)([ :a ], [ ' ' ])
end
data
end
keysAndValues ({a: 1, b: 2, c: 3})
def keysAndValues(data)
data.each.slice_to.a(2) { |x, y| [x], [y] }
end
data
end
keysAndValues ({a: 1, b: 2, c: 3})
def keysAndValues(data)
[data.keys, data.values]
end
keys, values = keysAndValues({a: 1, b: 2, c: 3})
keys
# => [:a, :b, :c]
values
# => [1, 2, 3]
Please note that keysAndValues is not following the Ruby naming convention. The correct name should be keys_and_values.
There are built-in methods for that:
irb(main):001:0> {a: 1, b: 2, c: 3}.keys
=> [:a, :b, :c]
irb(main):002:0> {a: 1, b: 2, c: 3}.values
=> [1, 2, 3]
Or, maybe you want this? (Sorry, your question is not clear to me)
irb(main):003:0> Array({a: 1, b: 2, c: 3})
=> [[:a, 1], [:b, 2], [:c, 3]]
def keys_and_values(data)
return data.keys, data.values
end
thus:
data_hash = {a: 1, b: 2, c: 3, d: 4}
keys_and_values(data_hash)
returns:
[[a:, b:, c:, d:], [1, 2, 3, 4]]

Ruby. "Magic method" for array of hashes

Community, how to implement method "m_met" which will reorganize array of hashes (with equal size and with equal "keys") to a form where 1st element will be an array of "keys" and rest - "values" of each hash.
Example:
m_met([{a: 1, b: 2, c: 3 }, {a: 4, b: 5, c: 6}])
# => [[:a, :b, :c], [1, 2, 3], [4, 5, 6]]
Thanks!
Without additional assumptions it could be something like:
hs = [{a: 1, b: 2, c: 3 }, {a: 4, b: 5, c: 6}]
ks = hs.first.keys
[ks] + hs.map{ |h| h.values_at(*ks)}
UPDATE Frankly I would prefer second line as (so I don't need equal whatever)
ks = hs.flat_map(&:keys).uniq
h = [{a: 1, b: 2, c: 3 }, {a: 4, b: 5, c: 6}]
h.flat_map { |j| [j.keys, j.values] }.uniq
# => [[:a, :b, :c], [1, 2, 3], [4, 5, 6]]
UPDATE
h = [{a: 1, b: 2, c: 3 }, {a: 1, b: 2, c: 3}]
[h.first.keys].concat(h.map(&:values))
# => [[:a, :b, :c], [1, 2, 3], [1, 2, 3]]
h = [{a: 1, b: 2, c: 3 }, {a: 4, b: 5, c: 6}]
[h.first.keys].concat(h.map(&:values))
# => [[:a, :b, :c], [1, 2, 3], [4, 5, 6]]

How do I create a custom "merge" method for hashes?

How do I implement a "custom_merge" method?
h1 = {a: 1, c: 2}
h2 = {a: 3, b: 5}
This is a standard "merge" method implementation:
h1.merge(h2) # => {:a=>3, :c=>2, :b=>5}
My desired "custom_merge" method should implement:
h1.custom_merge(h2) # {a: [1, 3], b: 5, c: 2}
No need the custom_merge method. Ruby core supplied Hash#merge with block will help you out.
h1 = {a: 1, c: 2}
h2 = {a: 3, b: 5}
h3 = h1.merge(h2){|k,o,n| [o,n]}
h3
# => {:a=>[1, 3], :c=>2, :b=>5}
class Hash
def custom_merge other
merge(other){|_, *a| a}
end
end

Resources