Looking to write this ruby code in one line - ruby

I want to display my hash in a string like this:
The results are a=100, b=200
When I loop through the hash like so:
a = [{:a => 100}, {:b => 200}]
a.each do |x|
x.each do |k,v|
puts "#{k}=#{v}"
end
end
the output returns strings in multiple lines
a=100
b=200
How can I change it to one string?

I was able to get it working with this
puts a.map{|x|x.map{|k,v|"#{k}=#{v}"}}.join(',')
Not sure if its the best solution though.

{:a => 100, :b => 200}.each { |k,v| puts "#{k}=#{v}" }

Related

Converting an array of strings into a key-value pair

I have an array as the value of a hash:
:params=>[":centre_id", ":id"]
I am trying to find a way to convert each element of the array to something like this:
:centre_id => 1
:id => 1
I tried loads of different ways but I can't seem to find a nice clean way to do it:
"#{route[:params].map {|x| x.parameterize.underscore.to_sym => '1'}}"
This is what I am trying to achieve:
-{"controller"=>"venues", "action"=>"activity, {:centre_id=>1, :id=>1}"}
+{"controller"=>"venues", "action"=>"activity", "centre_id"=>"1", "id"=>"1"} << this one
string or symbol doesn't matter does it?
Using this:
expect(route[:request_method] => route[:path]).to route_to "#{route[:controller]}##{route[:action]}, #{Hash[route[:params].map {|x| [x.sub(/\A:/,'').to_sym, 1] }]}"
Help would be much appreciated.
Do as below :-
Ruby 2.1 or above, use below :-
route[:params].map {|x| [x.sub(/\A:/,'').to_sym, 1] }.to_h
# => {:centre_id => 1, :id => 1}
Ruby 2.0 or below, use below :-
Hash[route[:params].map {|x| [x.sub(/\A:/,'').to_sym, 1] }]
# => {:centre_id => 1, :id => 1}
If
h = { :params=>[":centre_id", ":id"], :animals=>[":dog", ":cat", ":pig"] }
one of the following may be helpful:
h.merge(h) { |_,v,_| Hash[v.map { |s| [s[1..-1].to_sym,1] }] }
#=> {:params=>{:centre_id=>1, :id=>1}, :animals=>{:dog=>1, :cat=>1, :pig=>1}}
or
Hash[h.keys.map { |k| h.delete(k).map { |s| [s[1..-1].to_sym,1] } }.flatten(1)]
#=> {:centre_id=>1, :id=>1, :dog=>1, :cat=>1, :pig=>1}
The second modifies h. If you don't want that to happen, you'll need to h.dup first.
If a is an array, you can replace Hash[a] with a.to_h with Ruby 2.1+.

Is there a better solution to partition a hash into two hashes?

I wrote a method to split a hash into two hashes based on a criteria (a particular hash value). My question is different from another question on Hash. Here is an example of what I expect:
h={
:a => "FOO",
:b => "FOO",
:c => "BAR",
:d => "BAR",
:e => "FOO"
}
h_foo, h_bar = partition(h)
I need h_foo and h_bar to be like:
h_foo={
:a => "FOO",
:b => "FOO",
:e => "FOO"
}
h_bar={
:c => "BAR",
:d => "BAR"
}
My solution is:
def partition h
h.group_by{|k,v| v=="FOO"}.values.collect{|ary| Hash[*ary.flatten]}
end
Is there a clever solution?
There's Enumerable#partition:
h.partition { |k, v| v == "FOO" }.map(&:to_h)
#=> [{:a=>"FOO", :b=>"FOO", :e=>"FOO"}, {:c=>"BAR", :d=>"BAR"}]
Or you could use Enumerable#each_with_object to avoid the intermediate arrays:
h.each_with_object([{}, {}]) { |(k, v), (h_foo, h_bar)|
v == "FOO" ? h_foo[k] = v : h_bar[k] = v
}
#=> [{:a=>"FOO", :b=>"FOO", :e=>"FOO"}, {:c=>"BAR", :d=>"BAR"}]
I don't think there is a clever one liner, but you can make it slightly more generic by doing something like:
def transpose(h,k,v)
h[v] ||= []
h[v] << k
end
def partition(h)
n = {}
h.map{|k,v| transpose(n,k,v)}
result = n.map{|k,v| Hash[v.map{|e| [e, k]}] }
end
which will yield
[{:a=>"FOO", :b=>"FOO", :e=>"FOO"}, {:c=>"BAR", :d=>"BAR"}]
when run against your initial hash h
Edit - TIL about partition. Wicked.
Why not use builtin partition, which is doing almost exactly what you are looking for?
h_foo, h_bar = h.partition { |key, value| value == 'FOO' }
The only downside is that you will get arrays instead of hashes (but you already know how to convert that). In ruby 2.1+ you could simply call .map(&:to_h) at the end of call chain.

ruby sort_by method

I have just started to learn ruby. I have an array of hashes. I want to be able to sort the array based on an elementin the hash. I think I should be able to use the sort_by method. Can somebody please help?
#array of hashes
array = []
hash1 = {:name => "john", :age => 23}
hash2 = {:name => "tom", :age => 45}
hash3 = {:name => "adam", :age => 3}
array.push(hash1, hash2, hash3)
puts(array)
Here is my sort_by code:
# sort by name
array.sort_by do |item|
item[:name]
end
puts(array)
Nothing happens to the array. There is no error either.
You have to store the result:
res = array.sort_by do |item|
item[:name]
end
puts res
Or modify the array itself:
array.sort_by! do |item| #note the exclamation mark
item[:name]
end
puts array
You can do it by normal sort method also
array.sort { |a,b| a[:name] <=> b[:name] }
Above is for ascending order, for descending one replace a with b. And to modify array itself, use sort!
You can use sort by method in one line :
array.sort_by!{|item| item[:name]}

Create Nested Hashes from a List of Hashes in Ruby

I have a set of categories and their values stored as a list of hashes:
r = [{:A => :X}, {:A => :Y}, {:B => :X}, {:A => :X}, {:A => :Z}, {:A => :X},
{:A => :X}, {:B => :Z}, {:C => :X}, {:C => :Y}, {:B => :X}, {:C => :Y},
{:C => :Y}]
I'd like to get a count of each value coupled with its category as a hash like this:
{:A => {:X => 4, :Y => 1, :Z => 1},
:B => {:X => 2, :Z => 1},
:C => {:X => 1, :Y => 3}}
How can I do this efficiently?
Here's what I have so far (it returns inconsistent values):
r.reduce(Hash.new(Hash.new(0))) do |memo, x|
memo[x.keys.first][x.values.first] += 1
memo
end
Should I first compute the counts of all instances of specific {:cat => :val}s and then create the hash? Should I give a different base-case to reduce and change the body to check for nil cases (and assign zero when nil) instead of always adding 1?
EDIT:
I ended up changing my code and using the below method to have a cleaner way of achieving a nested hash:
r.map do |x|
[x.keys.first, x.values.last]
end.reduce({}) do |memo, x|
memo[x.first] = Hash.new(0) if memo[x.first].nil?
memo[x.first][x.last] += 1
memo
end
The problem of your code is: memo did not hold the value.
Use a variable outside the loop to hold the value would be ok:
memo = Hash.new {|h,k| h[k] = Hash.new {|hh, kk| hh[kk] = 0 } }
r.each do |x|
memo[x.keys.first][x.values.first] += 1
end
p memo
And what's more, it won't work to init a hash nested inside a hash directly like this:
# NOT RIGHT
memo = Hash.new(Hash.new(0))
memo = Hash.new({})
Here is a link for more about the set default value issue:
http://www.themomorohoax.com/2008/12/31/why-setting-the-default-value-of-a-hash-to-be-a-hash-is-wrong
Not sure what "inconsistent values" means, but your problem is the hash you're injecting into is not remembering its results
r.each_with_object(Hash.new { |h, k| h[k] = Hash.new 0 }) do |individual, consolidated|
individual.each do |key, value|
consolidated[key][value] += 1
end
end
But honestly, it would probably be better to just go to wherever you're making this array and change it to aggregate values like this.
Functional approach using some handy abstractions -no need to reinvent the wheel- from facets:
require 'facets'
r.map_by { |h| h.to_a }.mash { |k, vs| [k, vs.frequency] }
#=> {:A=>{:X=>4, :Y=>1, :Z=>1}, :B=>{:X=>2, :Z=>1}, :C=>{:X=>1, :Y=>3}}

Hash.each doesn't return a hash?

Hash.each returns an array [key, value],
but if I want a hash?
Example: {:key => value }
I'm assuming you meant "yields" where you said "return" because Hash#each already returns a hash (the receiver).
To answer your question: If you need a hash with the key and the value you can just create one. Like this:
hash.each do |key, value|
kv_hash = {key => value}
do_something_with(kv_hash)
end
There is no alternative each method that yields hashs, so the above is the best you can do.
I think you are trying to transform the hash somehow, so I will give you my solution to this problem, which may be not exactly the same. To modify a hash, you have to .map them and construct a new hash.
This is how I reverse key and values:
h = {:a => 'a', :b => 'b'}
Hash[h.map{ |k,v| [v, k] }]
# => {"a"=>:a, "b"=>:b}
Call .each with two parameters:
>> a = {1 => 2, 3 => 4}
>> a.each { |b, c|
?> puts "#{b} => #{c}"
>> }
1 => 2
3 => 4
=> {1=>2, 3=>4}
You could map the hash to a list of single-element hashes, then call each on the list:
h = {:a => 'a', :b => 'b'}
h.map{ |k,v| {k => v}}.each{ |x| puts x }

Resources