Get key/value pairs as arrays - ruby

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]]

Related

How to return a row twice inside map method

I would like to return a value twice inside a map method, only if (for example) the key == :b
I have something like this:
{a: 1, b: 2, c: 3}.map{ |x| x }
# => [[:a, 1], [:b, 2], [:c, 3]]
I would like to create this:
# => [[:a, 1], [:b, 2], [:b, 2], [:c, 3]]
I tried:
output = {a: 1, b: 2, c: 3}.map{ |x| x.first == :b ? [x,x] : x }
# => [[:a, 1], [[:b, 2], [:b, 2]], [:c, 3]]
output.flatten
# => [:a, 1, :b, 2, :b, 2, :c, 3]
output.flatten(1)
# => [:a, 1, [:b, 2], [:b, 2], :c, 3]
Any thoughts?
You can just write it in a single line
{a: 1, b: 2, c: 3}.map{ |x| x.first == :b ? [x,x] : [x] }.flatten(1)
{a: 1, b: 2, c: 3}.map{ |x| x.first == :b ? [x,x] : [x] }.flatten(1)
or
{a: 1, b: 2, c: 3}.reduce([]){ |memo, x| x.first == :b ? memo << x << x : memo << x }
or maybe a couple dozen other ways :)

Ruby - How to use Hash#to_proc?

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]

Challenge: combine into an array only sequential keys of specific value in Ruby

I want a function that takes parameters like this
list = [{a:1},{a:2},{b:3},{b:4},{c:5},{a:6}]
key = :a
combine_only_sequential_occurances_of_specific_key(list,key)
and would return this
[{a:[1,2]},{b:3},{b:4},{c:5},{a:6}]
Basically, combine a list of key/value pairs that occur sequentially, but limited only to a specific key (or if you like, a set of keys) and preserve order.
Thanks to the power of Enumerable, this is a rather easy task:
def combine_only_sequential_occurances_of_specific_key(list, *keys)
list.
chunk {|h| if keys.include?(k = h.keys.first) then k else :_alone end }.
# split into chunks by key
map {|k, hs| if k == :_alone || hs.size == 1 then hs.first else {k => hs.map(&:values).reduce(:concat)} end}
# transform into hash from key to "sum" (i.e. concatenation) of the values
end
list = [{a: 1}, {a: 2}, {b: 3}, {b: 4}, {c: 5}, {a: 6}]
key = :a
combine_only_sequential_occurances_of_specific_key(list, key)
# => [{a: [1, 2]}, {b: 3}, {b: 4}, {c: 5}, {a: 6}]
Code
def combine_only_blah_blah_blah(list, key)
list.flat_map(&:to_a).
slice_when { |(k1,_),(k2,_)| k1 != k2 }.
flat_map do |a|
k = a.first.first
(a.size > 1 && k == key) ? { k=>a.map(&:last) } : a.map { |b| [b].to_h }
end
end
Example
list = [{a: 1}, {a: 2}, {b: 3}, {b: 4}, {c: 5}, {a: 6}]
key = :a
combine_only_blah_blah_blah(list, key)
#=> [{:a=>[1, 2]}, {:b=>3}, {:b=>4}, {:c=>5}, {:a=>6}]
Explanation
For list and key above, the steps are as follows.
b = list.flat_map(&:to_a)
#=> [[:a, 1], [:a, 2], [:b, 3], [:b, 4], [:c, 5], [:a, 6]]
e = b.slice_when { |(k1,_),(k2,_)| k1 != k2 }
#=> #<Enumerator: #<Enumerator::Generator:0x007f9bda968c50>:each>
We can see what elements will be generated by this enumerator by converting it to an array.
e.to_a
#=> [[[:a, 1], [:a, 2]], [[:b, 3], [:b, 4]], [[:c, 5]], [[:a, 6]]]
Continuing,
e.flat_map do |a|
k = a.first.first
(a.size > 1 && k == key) ? { k=>a.map(&:last) } : a.map { |b| [b].to_h }
end
#=> [{:a=>[1, 2]}, {:b=>3}, {:b=>4}, {:c=>5}, {:a=>6}]
The first element generated by e that is passed to flat_map's block is
a = e.next
#=> [[:a, 1], [:a, 2]]
and the block calculation is as follows.
k = a.first.first
#=> :a
(a.size > 1 && k == key)
#=> (2 > 1 && :a == :a)
#=> true
so
{ k=>a.map(&:last) }
#=> {:a=>[1, 2]}
is executed. The next element generated by e and passed to the block, and the subsequent block calculations are as follows.
a = e.next
#=> [[:b, 3], [:b, 4]]
k = a.first.first
#=> :b
(a.size > 1 && k == key)
#=> (2 > 1 && :b == :a)
#=> false
a.map { |b| [b].to_h }
#=> [{:b=>3}, {:b=>4}]
Note that when
b = [:b, 3]
[b].to_h
#=> [[:b, 3]].to_h
#=> {:b=>3}
For Ruby versions prior to v2.0, when Array#to_h made its debut, use Hash::[].
Hash[[b]]
#=> {:b=>3}

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