Converting Ruby hashes to arrays - ruby-on-rails

I have a Hash which is of the form
{:a => {"aa" => 11,"ab" => 12}, :b => {"ba" => 21,"bb" => 22}}
How do i convert it to the form {:a => [["aa",11],["ab",12]],:b=>[["ba",21],["bb",22]]}

If you want to modify the original hash you can do:
hash.each_pair { |key, value| hash[key] = value.to_a }
From the documentation for Hash#to_a
Converts hsh to a nested array of [
key, value ] arrays.
h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
h.to_a #=> [["c", 300], ["a", 100], ["d", 400]]

Here is another way to do this :
hsh = {:a => {"aa" => 11,"ab" => 12}, :b => {"ba" => 21,"bb" => 22}}
hsh.each{|k,v| hsh[k]=*v}
# => {:a=>[["aa", 11], ["ab", 12]], :b=>[["ba", 21], ["bb", 22]]}

hash.collect {|a, b| [a, hash[a].collect {|c,d| [c,d]}] }.collect {|e,f| [e => f]}

Related

Ruby sort by hash and value

I have data like this:
hash_data = [
{:key1 => 'value4', :sortby => 4},
{:key1 => 'valuesds6', :sortby => 6},
{:key1 => 'valuedsd', :sortby => 1},
{:key1 => 'value2_data_is_here', :sortby => 2}
]
I want to sort it to this by the key sortby
hash_data = [
{:key1 => 'valuedsd', :sortby => 1},
{:key1 => 'value2_data_is_here', :sortby => 2},
{:key1 => 'value4', :sortby => 4},
{:key1 => 'valuesds6', :sortby => 6}
]
I have tried using bubble sort, but is there any inbuilt function in a Hash class for such purposes?
Enumerable#sort_by to the rescue:
hash_data.sort_by { |hash| hash[:sortby] }
#=> [{:key1=>"valuedsd", :sortby=>1}, {:key1=>"value2_data_is_here", :sortby=>2}, {:key1=>"value4", :sortby=>4}, {:key1=>"valuesds6", :sortby=>6}]
If you don't care about initial object, I would suggest using Array#sort_by! to modify inplace - it is more resource-efficient:
hash_data.sort_by! { |hash| hash[:sortby] }
If you have different types of data as values to sortby key, you should first unify the data type and only then perform sorting.
To have array sorted in descending order, use Enumerable#reverse (or reverse!):
hash_data.sort_by {|hash| hash[:sortby] }.reverse
#=> [{:key1=>"valuesds6", :sortby=>6}, {:key1=>"value4", :sortby=>4}, {:key1=>"value2_data_is_here", :sortby=>2}, {:key1=>"valuedsd", :sortby=>1}]
Another option for sorting in descending order is the following - note minus sign (credits to #sagarpandya82):
hash_data.sort_by {|hash| -hash[:sortby] }

How to permit nested hash parameters with the StrongParameters gem?

I am using Ruby on Rails 4.1 and I would like to permit the following incoming parameters by using the StrongParameters gem:
# Parameters:
{
"my_key" => {
"one" => {
"0" => { "a" => "a_value", "b" => "b_value"},
"1" => { "a" => "a_value", "b" => "b_value"},
"2" => { "a" => "a_value", "b" => "b_value"}
},
"two" => {
"0" => { "c" => "c_value", "d" => "d_value"},
"1" => { "c" => "c_value", "d" => "d_value"},
"2" => { "c" => "c_value", "d" => "d_value"}
}
}
}
In controller I tried
params
.require(:my_key)
.permit(
[
:one => [
"0" => [:a, :b],
"1" => [:a, :b],
"2" => [:a, :b]
],
:two => [
"0" => [:c, :d],
"1" => [:c, :d],
"2" => [:c, :d]
]
]
)
and
params
.require(:my_key)
.permit(
{
:one => {
"0" => [:a, :b],
"1" => [:a, :b],
"2" => [:a, :b]
},
:two => {
"0" => [:c, :d],
"1" => [:c, :d],
"2" => [:c, :d]
}
}
)
But I get the error
ActionController::UnpermittedParameters (found unpermitted parameters: a, b)
How above parameters should be permitted?
Here is what you need to do:
Remove Strong Parameters gem from your Gemfile.
Use this in the controller.
params.require(:my_key).permit({:one=>[:a, :b],:two=>[:c, :d]})
I think this has something to do with how nested attributes work. The ids "0", "1", "2" etc are implicit.
You can test in the console like this:
$ bin/rails c
Loading development environment (Rails 4.1.2)
2.1.0 :001 > params = ActionController::Parameters.new "my_key"=>{"one"=>{"0"=>{"a"=>"a_value","b"=>"b_value"},"1"=>{"a"=>"a_value","b"=>"b_value"},"2"=>{"a"=>"a_value","b"=>"b_value"}},"two"=>{"0"=>{"c"=>"c_value","d"=>"d_value"},"1"=>{"c"=>"c_value","d"=>"d_value"},"2"=>{"c"=>"c_value","d"=>"d_value"}}}
=> {"my_key"=>{"one"=>{"0"=>{"a"=>"a_value", "b"=>"b_value"}, "1"=>{"a"=>"a_value", "b"=>"b_value"}, "2"=>{"a"=>"a_value", "b"=>"b_value"}}, "two"=>{"0"=>{"c"=>"c_value", "d"=>"d_value"}, "1"=>{"c"=>"c_value", "d"=>"d_value"}, "2"=>{"c"=>"c_value", "d"=>"d_value"}}}}
2.1.0 :002 > p = params.require(:my_key).permit({:one=>[:a, :b],:two=>[:c, :d]})
=> {"one"=>{"0"=>{"a"=>"a_value", "b"=>"b_value"}, "1"=>{"a"=>"a_value", "b"=>"b_value"}, "2"=>{"a"=>"a_value", "b"=>"b_value"}}, "two"=>{"0"=>{"c"=>"c_value", "d"=>"d_value"}, "1"=>{"c"=>"c_value", "d"=>"d_value"}, "2"=>{"c"=>"c_value", "d"=>"d_value"}}}
2.1.0 :003 >

How do I get the unique elements and count from an array of hashes in Ruby?

I have an array of hashes, and I want the unique values and count out of it.
I have:
a = [{:key => 2}, {:key => 1}, {:key => 4}, {:key => 1}]
I want see:
a = [{:key => 2}, {:key => 1, :count =>2}, {:key => 4}]
Try this:
a = [{:key => 2}, {:key => 1}, {:key => 4}, {:key => 1}]
b = a.clone
a.uniq!
a.inject([]) { |result,h| h[:count]=b.count(h) if b.count(h) > 1 ; result << h; result }
=> [{:key=>2}, {:key=>1, :count=>2}, {:key=>4}]
You will just have to iterate over the array and count how many times each key occurs, and then sum it all together by building a new array with the result.
The below code snippet should do it.
a = [{:key => 2}, {:key => 1}, {:key => 4}, {:key => 1}]
counts = Hash.new(0)
a.map do |item|
counts[item[:key]] += 1
end
a = counts.collect do |key, count|
if count > 1
{:key => key, :count => count}
else
{:key => key}
end
end
Try this:
a.each {|h| count = a.count(h); if count > 1; dup_h = h.clone; h.merge!({:count => count}); a.delete(dup_h); end }
The most concise way there is (I think) is this:
a.uniq.map{|hsh| c = a.count(hsh); c == 1 || hsh[:count] = c; hsh}
or if you don't want to modify the hashes in a:
a.uniq.map{|hsh| h = hsh.dup; c = a.count(hsh); c == 1 || h[:count] = c; h}
A (marginally) prettier solution that yields a different result is:
a.uniq.group_by{|hsh| a.count(hsh)}
#=> {1=>[{:key=>2}, {:key=>4}], 2=>[{:key=>1}]}
Check out the documentation on Enumerable#count and Enumerable#group_by to learn more of what you can do with those.
Ruby's core is actually quite powerful.

In Rails, what is the best way to compact a hash into a nested hash

Say I have this:
[
{ :user_id => 1, :search_id => a},
{ :user_id => 1, :search_id => b},
{ :user_id => 2, :search_id => c},
{ :user_id => 2, :search_id => d}
]
and I want to end up with:
[
{ :user_id => 1, :search_id => [a,b]},
{ :user_id => 2, :search_id => [c,d]}
]
What is the best way to do that?
Very strange requirement indeed. Anyway
[ { :user_id => 1, :search_id => "a"},
{ :user_id => 1, :search_id => "b"},
{ :user_id => 2, :search_id => "c"},
{ :user_id => 2, :search_id => "d"} ] \
.map{ |h| h.values_at(:user_id, :search_id) } \
.group_by(&:first) \
.map{ |k, v| { :user_id => k, :search_id => v.map(&:last) } }
array.group_by{|x| x[:user_id] }.values.map do |val|
{ user_id: val.first[:user_id],
search_id: val.inject([]){|me, el| me << el[:search_id]} }
end
First off, I think the cleaner output structure here is to allow the user IDs to be the hash keys and the list of search IDs to be the values:
{
1 => [a, b],
2 => [c, d]
}
There might be a clever way using Rails helpers to get this structure, but it's not too bad to do it manually, either:
output = {}
input.each do |row|
key = row[:user_id]
value = row[:search_id]
output[key] ||= []
output[key] << value
end
I agree with Matchus representation of the data and just want to suggest a shorter version for it (input being the initial array).
input.each.with_object(Hash.new {|h,k| h[k] = []}) do |k,o|
o[k[:user_id]] << k[:search_id]
end
EDIT: this is Ruby > 1.9
input.inject({}) do
|m, h| (m[h[:user_id]] ||= []) << h[:search_id]; m
end.inject([]) { |m, (k, v)| m << { :user_id => k, :search_id => v }; m }

ruby on Rails: Merge 2 activerecord arrays

I Have 2 arrays such as:
arr1 : {[:day => 12, :sum_src => 1234], [:day => 14, :sum_src => 24543]}
arr2 : {[:day => 12, :sum_dst => 4234], [:day => 14, :sum_dst => 342334]}
I want to merge this two arrays into one, so that it looks like:
arr3 : {[:day => 12, :sum_src => 1234, :sum_dst => 4234],[:day => 14, :sum_src => 24543, :sum_dst => 342334]}
Is it possible? And how to do this ?
Riffing off Qerub's answer - if the arrays are sorted as in the example zip can be a great tool for this:
arr1 = [{:day => 12, :sum_src => 1234}, {:day => 14, :sum_src => 24543}]
arr2 = [{:day => 12, :sum_dst => 4234}, {:day => 14, :sum_dst => 342334}]
arr1.zip(arr2).map {|a,b| a.merge!(b)}
Result
[{:day=>12, :sum_dst=>4234, :sum_src=>1234}, {:day=>14, :sum_dst=>342334, :sum_src=>24543}]
Like Omar pointed out, those are hashes. If you have two hashes:
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
In your case, if you want to merge to arrays and remove repetitions, I would use Union operator:
[ "a", "b", "c" ] | [ "c", "d", "a" ] #=> [ "a", "b", "c", "d" ]
Taken from Rails API.
Good luck!
(You have mixed up the syntaxes for literal hashes and arrays, but anyway…)
Here's a short but slightly cryptic solution:
arr1 = [{:day => 12, :sum_src => 1234}, {:day => 14, :sum_src => 24543}]
arr2 = [{:day => 12, :sum_dst => 4234}, {:day => 14, :sum_dst => 342334}]
result = (arr1 + arr2).inject({}) { |mem, x|
(mem[x[:day]] ||= {}).merge!(x); mem
}.values
Result:
[{:sum_dst=>4234, :day=>12, :sum_src=>1234}, {:sum_dst=>342334, :day=>14, :sum_src=>24543}]
A more readable version would probably have more explicit looping. (Left for the reader as an exercise.)

Resources