Rails/Ruby ordering / splitting it - ruby-on-rails

<%
old_city = ""
#objects.order("city").each do |obj|
if old_city != obj.city && old_city != ""
old_city = obj.city
%>
--Different city--
<%
end
%>
City: <%= obj.city %>
<%
end
%>
So that output expected is:
Chicago
Chicago
--Different city--
New York
New York
New York
--Different city--
Paris
--Different city--
Rio de Janeiro
Maybe there's some cleaver/different way to do that in rails?
I don't think this is the best code for it...
Thanks!

There are several options, but Enumerable offers a group_by method.
group_by takes a block to define groupings. After grouping it's a matter of iterating over the resulting map's keys.
objs = [
{ :foo => 'baz', :wat => 'kthxbai' },
{ :foo => 'bar', :wat => 'narnar' },
{ :foo => 'plugh', :wat => 'xyzzy' },
{ :foo => 'bar', :wat => 'ohai' },
{ :foo => 'baz', :wat => 'fuuuu' },
{ :foo => 'plugh', :wat => 'jimmies' }
]
grouped = objs.group_by { |o| o[:foo] }
grouped.each do |k, v|
puts "GROUP: #{k}"
v.each { |v| puts v }
end
If you want to order by keys, you can do that too by sorting the keys and retrieving the resulting map's values while iterating over the sorted keys.
If they're ActiveRecords you might want to do the work in SQL/ActiveRecord proper.

Try something like this in the console:
Event.order(:city).group_by(&:city)
This will return a hash where the keys will be the individual cities and the values will be arrays of the corresponding event objects. You can then easily iterate over the hash's keys, and in an inner loop, iterate over the corresponding event objects.

Related

Sort items in a nested hash by their key

I have a nested hash with unsorted keys:
given = {
"lorem" => {
:AA => "foo",
:GR => "foo",
:BB => "foo"
},
"ipsum" => {
:ZZ => "foo",
:GR => "foo",
}
}
What I'm trying to accomplish is a hash with sorted keys:
goal = {
"ipsum" => {
:GR => "foo",
:ZZ => "foo"
},
"lorem" => {
:AA => "foo",
:BB => "foo",
:GR => "foo"
}
}
I have experimented with .each method and sort_by
given.each { |topic| topic[:key].sort_by { |k, v| k } }
But I'm getting an error message: TypeError: no implicit conversion of Symbol into Integer
Any help is greatly appreciated!
PS: I noticed with gem pry the output is already sorted. But in IRB it's not.
You can use group_by, and transform_values to transform the values inside each hash, also using sort_by plus to_h:
given.transform_values { |value| value.sort.to_h }.sort.to_h
# {"ipsum"=>{:GR=>"foo", :ZZ=>"foo"}, "lorem"=>{:AA=>"foo", :BB=>"foo", :GR=>"foo"}}
You're getting an error because when iterating over a hash, you have to local variables within the block scope to use, the key and its value, you're assigning only one (topic) and trying to get its key, which would be trying to access a key in:
["lorem", {:AA=>"foo", :GR=>"foo", :BB=>"foo"}]
Which isn't possible as is an array. You can update your code to:
given.each do |topic, value|
...
end
But anyway you'll need a way to store the changes or updated and sorted version of that topic values.
given_hash = {"lorem"=>{:AA=>"foo", :GR=>"foo", :BB=>"foo"}, "ipsum"=>{:ZZ=>"foo", :GR=>"foo"}}
Get keys
given_hash.keys
=> ["lorem", "ipsum"]
New sorted hash
new_hash = {}
given_hash.keys.sort.each do |sorted_key|
new_hash[sorted_key] = given[sorted_key]
end
=> {"ipsum"=>{:ZZ=>"foo", :GR=>"foo"}, "lorem"=>{:AA=>"foo", :GR=>"foo", :BB=>"foo"}}
There can be a better way to do this.

How to create 1- and 2-dimensional hash

I am loading data from database and saving it to a constant like this:
profession = Hash.new
Profession.all.each do |p|
profession[p.name] = p.unique_profession
profession[p.name]['descr'] = p.description # problem
profession[p.name]['created_at'] = p.created_at # problem
end
On the line profession[p.name]['descr'] occurs this error:
undefined method `[]=' for 1:Fixnum (NoMethodError)
I want to use profession like:
<div>Profession name: <%= profession[p.name] %></div>
<div>Description: <%= profession[p.name]['descr'] %></div>
How can I make profession work with as one [name] and as two [name][descr] parameters?
You start with an empty hash:
{}
Then, for every Profession record, you assign profession[p.name] = p.unique_profession. Assuming that unique_profession is a Fixnum, that means you get this:
{
'ProfessionName1' => 1,
'ProfessionName2' => 2
}
and so on.
You can't assign sub-keys to Fixnum - it's not a hash. You might instead want one of the following data structures (I'll follow the structure with the code to create it):
{
'ProfessionName1' => {
'descr' => 'Description of Profession 1',
'created_at' => '2016-05-08T22:33:38.093753Z'
},
'ProfessionName2' => {
'descr' => 'Description of Profession 2',
'created_at' => '2015-04-09T21:23:33.093753Z'
}
}
profession = Hash.new
Profession.all.each do |p|
profession[p.name] = Hash.new
profession[p.name]['descr'] = p.description
profession[p.name]['created_at'] = p.created_at
end
or simply an array of hashes containing every property:
[
{
'name' => 'ProfessionName1',
'descr' => 'Description of Profession 1',
'created_at' => '2016-05-08T22:33:38.093753Z'
},
{
'name' => 'ProfessionName2',
'descr' => 'Description of Profession 2',
'created_at' => '2015-04-09T21:23:33.093753Z'
}
]
profession = []
Profession.all.each do |p|
profession << {
'name' => p.name,
'descr' => p.description,
'created_at' => p.created_at
}
end
If you want to use hashes you'll need to store 'unique_profession' into a new key.
profession = Hash.new
Profession.all.each do |p|
profession[p.name] = []
profession[p.name]['unique'] = p.unique_profession
profession[p.name]['descr'] = p.description # problem
profession[p.name]['created_at'] = p.created_at # problem
end
Then you can do this (but note the output will be a number, the contents of unique_profession):
<div>Profession name: <%= profession[p.name]['unique'] %></div>
<div>Description: <%= profession[p.name]['descr'] %></div>
Finally, I'd recommend using symbols instead of strings (as that's the Ruby way):
profession = Hash.new
Profession.all.each do |p|
profession[p.name] = []
profession[p.name][:unique] = p.unique_profession
profession[p.name][:descr] = p.description
profession[p.name][:created_at] = p.created_at
end
Then
<div>Profession name: <%= profession[p.name][:unique] %></div>
<div>Description: <%= profession[p.name][:descr] %></div>
On the other hand you could just store and use the Profession instances directly.

Merge two arrays that both have key value pairs (Ruby)

I am wondering how to merge these two arrays into one clean array in Ruby
Both arrays share one similar key:value pair. I am trying to merge information from these two separate arrays that have information for the same person. One array has his name. The other array has his job and age. Both arrays have an id matching to the same person.
An example of what I am trying to do
array1 = [ {:id => 1, :name => "Bob"}, {:id => 2, :name => "Tim"}]
array2 = [ {:id => 1, :job => "firefighter", :age => 25}, { :id => 2, :job => "accountant", :age => 30} ]
new_array = [ {:id=> 1, name => "Bob", :job => "firefighter", :age => 25}, { :id => 2, :name => "Tim", :job => "accountant", :age => 30} ]
You could do something like this:
new_array = array1.each_with_index.map { |x, i| x.merge array2[i] }
# => [{:id=>1, :name=>"Bob", :job=>"firefighter", :age=>25}, {:id=>2, :name=>"Tim", :job=>"accountant", :age=>30}]
If you want a solution that is not dependent on the order of the array, and instead uses the :id to match the hashes:
array1.map { |x| x.merge (array2.find { |h| h[:id] == x[:id] } || {}) }
If the two arrays contain the same :id values at the same locations:
array1.zip(array2).map { |g,h| g.merge(h) }
#=> [{:id=>1, :name=>"Bob", :job=>"firefighter", :age=>25},
# {:id=>2, :name=>"Tim", :job=>"accountant", :age=>30}]
or equivalently:
[array1, array2].transpose.map { |g,h| g.merge(h) }
If the two arrays contain the same :id values but not necessarily at the same locations:
(array1 + array2).group_by { |h| h[:id] }
.values
.map { |g,h| g.merge(h) }
or
array1.sort_by { |h| h[:id] }
.zip(array2.sort_by { |h| h[:id] } )
.map { |g,h| g.merge(h) }

tree structure in ruby with parent child in array format without gems?

I have a array which have list of item like this
arr = [
{:id=>1, :title=>"A", :parent_id=>nil},
{:id=>2, :title=>"B", :parent_id=>nil},
{:id=>3, :title=>"A1", :parent_id=>1},
{:id=>4, :title=>"A2", :parent_id=>1},
{:id=>5, :title=>"A11", :parent_id=>3},
{:id=>6, :title=>"12", :parent_id=>3},
{:id=>7, :title=>"A2=121", :parent_id=>6},
{:id=>8, :title=>"A21", :parent_id=>4},
{:id=>9, :title=>"B11", :parent_id=>2},
{:id=>10, :title=>"B12", :parent_id=>2},
...
]
If parent_id is nil then its should be the parent node, if parent_id is not nil then it should comes under the particular parent.
Based on id and parent_id, I want to provide a response like this:
-A
-A1
-A11
-A12
-A123
-A2
-A21
-B
-B1
-B11
-B12
How could I generate a responds mentioned above?
This is easier than you think, you just need to realize a couple simple things:
nil is a perfectly valid Hash key.
You can use nil as a virtual root for your tree so that all the :parent_ids point at things in your tree.
You can iterate through the array and track entries in two ways at once: by :id and by :parent_id.
First a tree represented by a Hash:
tree = Hash.new { |h,k| h[k] = { :title => nil, :children => [ ] } }
We're going to be going from the root to the leaves so we're only interested in the children side of the parent/child relationship, hence the :children array in the default values.
Then a simple iteration that fills in the :titles and :children as it goes:
arr.each do |n|
id, parent_id = n.values_at(:id, :parent_id)
tree[id][:title] = n[:title]
tree[parent_id][:children].push(tree[id])
end
Note that the nodes (including the parent nodes) are automatically created by tree's default_proc the first time they're seen so the node order in arr is irrelevant.
That leaves us with the tree in tree where the keys are :ids (including the virtual root at the nil key) and the values are subtrees from that point.
Then if you look at tree[nil][:children] to peel off the virtual root, you'll see this:
[
{ :title => "A", :children => [
{ :title => "A1", :children => [
{ :title => "A11", :children => [] },
{ :title => "12", :children => [
{ :title => "A2=121", :children => [] }
] }
] },
{ :title => "A2", :children => [
{ :title => "A21", :children => [] }
] }
] },
{ :title => "B", :children => [
{ :title => "B11", :children => [] },
{ :title => "B12", :children => [] }
] }
]
and that has exactly the structure you're looking for and you should be able to take it from there. That doesn't match your sample response but that's because your sample arr doesn't either.
You could also say:
tree = arr.each_with_object(Hash.new { |h,k| h[k] = { :title => nil, :children => [ ] } }) do |n, tree|
#...
end
if you preferred that rather noisy first line to a separate tree declaration.
Some thing like this will work:
parents = arr.select{|hash| hash[:parent_id] == nil }
parents.each {|hash| print_children hash, arr, "-"}
def print_children(hash, arr, spaces)
puts spaces + hash[:title]
spaces = ' ' + spaces
children = arr.select{|all_hash| all_hash[:parent_id] == hash[:id] }
children.each { |child_hash| print_children child_hash, arr, spaces }
end

dynamic loop in a hash Ruby on Rails

I'm trying to create a dynamic loop within the hash #data below and
can't really seem to figure it out. I'm creating an annotatedtimeline-for-rails using the google api from here https://github.com/mcommons/annotatedtimeline-for-rails.
The array within the hash #data has to be dynamic i:e the day number has to be generated by a loop and the name of the product and number are dynamic as well. I'll
try to give an example in the loop below
#numdeployed is a number and comes from a table in the db
i should be generated by the loop
#data{
begin loop
i.day.ago.to_date => { :foo=>#numdeployed, :bar=>#numdeployed, :barbaz=>#numdeployed, :foobar=>#numdeployed },
end loop
}
The Original Data Hash looks like this
#data = {
1.day.ago.to_date => { :foo=>10, :bar=>40, :barbaz=>10, :foobar=>40 },
2.day.ago.to_date => { :foo=>10, :bar=>40, :barbaz=>10,:foobar=>40 },
3.day.ago.to_date => { :foo=>10, :bar=>40, :barbaz=>10,:foobar=>40 },
4.day.ago.to_date => { :foo=>10, :bar=>40, :barbaz=>10,:foobar=>40 },
5.day.ago.to_date => { :foo=>10, :bar=>40, :barbaz=>10,:foobar=>40 }
}
hope someone can help. Thanks
Are you looking for something like this?
#data = Hash[
n.times.map do |i|
[ (i + 1).day.ago.to_date, { :foo => 10, :bar => 40, :barbaz => 10, :foobar => 40 } ]
end
]
The n is however many pairs you want in your #data.

Resources