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.)
Related
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] }
Given a Table/Model called Assignment with the fields:
id
student_id
grade (e.g. A, B, C)
... some other stuff
How can I get a list of the students and how many of each grade they have?
The nearest I've got has been:
Assignment.group(:student_id).group(:grade).count
But this gives me the data in the format:
{[student_id, grade] => count, [student_id, grade] => count, ...}
eg.
{
[1, "A"] => 8,
[1, "B"] => 6,
[2, "A"] => 7,
[2, "F"] => 5
}
Is there a way I can get the array to be on the value side so I can easily loop over and print out the students results? i.e. like this:
{
1 => {"A" => 8, "B" => 6},
2 => {"A" => 7, "F" => 5}
}
The nearest you've got is the final one. Now, you need to slightly reindex results:
student_grades_count = {
[1, "A"] => 8,
[1, "B"] => 6,
[2, "A"] => 7,
[2, "F"] => 5
}
student_grades_count = student_grades_count.reduce({}) do |sum, ((student_id, grade), count)|
sum[student_id] ||= {}
sum[student_id][grade] = count
sum
end
I would like to transform this
def some_process(k,v)
return "#{v}_#{k}"
end
a_hash = {
"i_love_hashes" => {
"thing" => 20,
"other_thing" => "0",
"yet_another_thing" => "i disagree",
"_peculiar_thing" => [
{"count" => 30,
"name" => "freddie"},
{"count" => 15,
"name" => "johhno"},
{"count" => 12,
"name" => "mohammed"},
]
},
"as_do_i" => {
"thing" => 10,
"other_thing" => "2",
"yet_another_thing" => "i strongly agree",
"_peculiar_thing" => [
{"count" => 10,
"name" => "frodo"},
{"count" => 4,
"name" => "bilbo"},
{"count" => 2,
"name" => "elizabeth"},
]
}
}
into this
{
"i_love_hashes"=>{
"thing"=>20,
"other_thing"=>"0",
"yet_another_thing"=>"i disagree",
"_peculiar_thing"=> [
{"count"=>30, "name"=>"freddie", :sinister_name=>"freddie_i_love_hashes"},
{"count"=>15, "name"=>"johhno", :sinister_name=>"johhno_i_love_hashes"},
{"count"=>12, "name"=>"mohammed", :sinister_name=>"mohammed_i_love_hashes"}
]},
"as_do_i"=>{
"thing"=>10,
"other_thing"=>"2",
"yet_another_thing"=>"i strongly agree",
"_peculiar_thing"=>[
{"count"=>10, "name"=>"frodo", :sinister_name=>"frodo_as_do_i"},
{"count"=>4, "name"=>"bilbo", :sinister_name=>"bilbo_as_do_i"},
{"count"=>2, "name"=>"elizabeth", :sinister_name=>"elizabeth_as_do_i"}
]
}
}
this is the code I am currently using to achieve this
a_hash.each_with_object({}) do |(k,v),o|
o.merge!({k =>
v.each_with_object({}) do |(a,b),g|
g.merge!({ a =>
(b.is_a?(Array) ? b.collect {|x| x.merge({sinister_name: (some_process k, x["name"])})} : b)
})
end
})
end
Ignoring the specific details of what is being returned by "some_process" (what is important is that it depends on the outer most key and the inner name values, in this example), are there any alternatives that would be considered more elegant?
Why not do a recursive function?
def add_siniter(hash)
hash[:siniter_name] = "#{hash['name']}_i_love_hashes"
hash
end
def format_hash(item)
case item
when Hash then item.keys.each{|key| format_hash(item[key])}
when Array then item.map!{|h| add_siniter(h)}
end
end
format_hash(a_hash)
In this post, slice function is used to get only necessary elements of params. What would be the function I should use to exclude an element of params (such as user_id)?
Article.new(params[:article].slice(:title, :body))
Thank you.
Use except:
a = {"foo" => 0, "bar" => 42, "baz" => 1024 }
a.except("foo")
# returns => {"bar" => 42, "baz" => 1024}
Inspired in the sourcecode of except in Rails' ActiveSupport
You can do the same without requiring active_support/core_ext/hash/except
# h.slice( * h.keys - [k1, k2...] )
# Example:
h = { a: 1, b: 2, c: 3, d: 4 }
h.slice( * h.keys - [:b, :c] ) # => { a: 1, d: 4}
Try this
params = { :title => "title", :other => "other", :body => "body" }
params.select {|k,v| [:title, :body].include? k } #=> {:title => "title", :body => "body"}
Considering only standard Ruby.
For Ruby versions below 3, no.
For Ruby 3, yes. You can use except.
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]}