Rails collection to nested json - ruby-on-rails

I need to convert rails collection to nested json.
Collection:
[
id: 2, name: "Dir 1", parent_id: nil, lft: 1, rgt: 6, depth: 0,
id: 3, name: "Dir 2", parent_id: nil, lft: 7, rgt: 8, depth: 0,
id: 9, name: "Dir 2.1", parent_id: 2, lft: 2, rgt: 3, depth: 1,
id: 10, name: "Dir 2.2", parent_id: 2, lft: 4, rgt: 5, depth: 1
...
]
output json
[
{ label: "Dir 1", children:[] },
{ label: "Dir 2", children: [
label: "Dir 2.1", children: [],
label: "Dir 2.2", children: []
]}
...
]

This is assuming your collection is tied to a model and you're using awesome_nested_set.
class Model
def self.collection_to_json(collection = roots)
collection.inject([]) do |arr, model|
arr << { label: model.name, children: collection_to_json(model.children) }
end
end
end
# usage: Model.collection_to_json
See here for roots.
An alternative to the above, because awesome_nested_set appears to produce queries on model.children is:
class Model
def self.parents_from_collection
all.select { |k,v| k[:depth] == 0 }
end
def self.children_from_collection(parent)
all.select { |k,v| k[:parent_id] == parent[:id] }
end
def self.collection_to_json(collection = false)
collection ||= parents_from_collection
collection.inject([]) do |arr, row|
children = children_from_collection(row)
arr << { label: row[:name], children: collection_to_json(children) }
end
end
end

try the to_json method
collection.to_json

Related

Get objects only within one array and not multiple arrays within an array

Sorry for the bad English Title but the code below would make more sense.
I used .each to get some keys and values that I wanted from the data but when I do it I get this result.
[
[
{
id: 1,
name: "Immad",
age: 18
}
],
[
{
id: 2,
name: "Vicky",
age: 21
}
],
[
{
id: 3,
name: "Adam",
age: 24
}
]
]
I want to map the objects to some js library for which I want it to be like:
[
{
id: 1,
name: "Immad",
age: 18
},
{
id: 2,
name: "Vicky",
age: 21
},
{
id: 3,
name: "Adam",
age: 24
}
]
Can anybody please atleast give me hint what should I use in order to do it using ruby. Thanks in advance.
There already is an Array method.
foo_array.flatten!
or non destructive (just a return value foo_array remains unchanged)
foor_array.flatten
Simple, You should do like this
Test = [[{:id=>1, :name=>"Immad", :age=>18}], [{:id=>2, :name=>"Vicky", :age=>21}], [{:id=>3, :name=>"Adam", :age=>24}]]
Use .flatten here.
Test.flatten
=> [{:id=>1, :name=>"Immad", :age=>18}, {:id=>2, :name=>"Vicky", :age=>21}, {:id=>3, :name=>"Adam", :age=>24}]
Use .map!:
foo = [
[
{
id: 1,
name: "Immad",
age: 18
}
],
[
{
id: 2,
name: "Vicky",
age: 21
}
],
[
{
id: 3,
name: "Adam",
age: 24
}
]
]
# Call .map! on the array
foo.map! { |array| array.first }
=> [
[0] {
:id => 1,
:name => "Immad",
:age => 18
},
[1] {
:id => 2,
:name => "Vicky",
:age => 21
},
[2] {
:id => 3,
:name => "Adam",
:age => 24
}
]

Optimize formatting from aggregated return from Sequel

I have a query from sequel that is returning something like this:
posts = [
<Post: #attributes={ id: 1, title: 'Foo', text: 'Bar', user_id: 21, user: <User: #attributes={ id: 21, name: 'John'}>}>,
<Post: #attributes={ id: 2, title: 'Bar', text: 'Foo', user_id: 21, user: <User: #attributes={ id: 21, name: 'John'}>}>,
<Post: #attributes={ id: 3, title: 'FooBar', text: 'FooBar', user_id: 19, user: <User: #attributes={ id: 19, name: 'Jane'}>}>
]
An array of Post and User objects.
I want to return it like this to the user:
json = {
posts:[
{ id: 1, title: 'Foo', text: 'Bar', user_id: 21 },
{ id: 2, title: 'Bar', text: 'Foo', user_id: 21 },
{ id: 3, title: 'FooBar', text: 'FooBar', user_id: 19 }
],
users: [
{ id: 21, name: 'John'},
{ id: 19, name: 'Jane'}
]
}
What would be the most efficient way to extract this Hash from the original array?
This is the code I'm using for it right now:
def prepare_json(array)
posts = []
users = Hash[]
array.each do |item|
posts.push(item.post)
# user id is unique so I use it to avoid duplication on
# the users array
users[item.user.id.to_sym] = item.user
end
{ posts: posts, users: users.values }
end
users = posts.map{|h| h.delete(:user)}.uniq
json = {posts: posts, users: users}
Result:
{
:posts=>[{:id=>1, :title=>"Foo", :text=>"Bar", :user_id=>21}, {:id=>2, :title=>"Bar", :text=>"Foo", :user_id=>21}, {:id=>3, :title=>"FooBar", :text=>"FooBar", :user_id=>19}],
:users=>[{:id=>21, :name=>"John"}, {:id=>19, :name=>"Jane"}]
}

how to covert array object to hash in rails

i have a hash with array object :
{
false=>[#<Campaign id: 1, name: "campaign 1", active: false>, #<Campaign id: 3, name: "campaign 3", active: false>, #<Campaign id: 4, name: "campaign 4", active: false>],
true=>[#<Campaign id: 2, name: "campaign 2", active: true>]
}
how to convert above hash to hash
{
false=>[{id:1, name:"campaign 1"}, {id:3, name: "capaign 3"}, ....],
true =>[{id:2, name:"campaign 2"}]
}
hash.each {|k,v| hash[k] = v.map{|e| {id: e[:id], name: e[:name]}}}
and if you can use select_all method get the array of hash, not array of object, so you doesn't need to covert object to hash.
ModelName.connection.select_all("select id, name from <table_name>;")
=> [{id:xxx, name: xxx}.......]
Use attributes method on your object
attributes() public
Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
hash = {
false => [#<Campaign id: 1, name: "campaign 1", active: false>, #<Campaign id: 3, name: "campaign 3", active: false>, #<Campaign id: 4, name: "campaign 4", active: false>],
true => [#<Campaign id: 2, name: "campaign 2", active: true>]
}
So this line should do the trick -
hash.each {|k, v| hash[k] = v.map(&:attributes) }
{
false => [{"id": 1, "name": "campaign 1", "active": false}, {"id": 3, "name": "campaign 3", "active": false}, {"id": 4, "name": "campaign 4", "active": false}],
true => [{"id": 2, "name": "campaign 2", "active": true}]
}

Group orderring

I am looking to group a list of projects by a nested field, in this case custom_field.value when a certain id is passed in.
[{
id: 1,
name: "project one ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2010"
},
{
id: 5,
name: "Priority",
value: "low"
},
]},
{
id: 2,
name: "project two ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2011"
},
{
id: 5,
name: "Priority",
value: "medium"
},
]},
{
id: 3,
name: "project three ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2012"
},
{
id: 5,
name: "Priority",
value: "high"
},
]}]
So if the params[:id] == 4 I want the list to be ordered by the custom_field id's corresponding value in decending order.
So in this case they would be ordered.
2012
2011
2010
Any ideas?
Is this what you are looking for? Your question is a bit unclear but I think this should suffice:
Your Original Hash:
test = [{
id: 1,
name: "project one ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2010"
},
{
id: 5,
name: "Priority",
value: "low"
},
]},
{
id: 2,
name: "project two ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2011"
},
{
id: 5,
name: "Priority",
value: "medium"
},
]},
{
id: 3,
name: "project three ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2012"
},
{
id: 5,
name: "Priority",
value: "high"
},
]}]
Use group_by and sort(with handling for elements where there is no id found):
def group_and_sort(test_hash,id)
test_hash.group_by do |g|
elem = g[:custom_fields].detect {|h| h[:id] == id}
elem ? elem[:value] : "0"
end.sort.reverse.to_h
end
Then call like:
group_and_sort(test,4)
#=>{"2012"=>
[{:id=>3,
:name=>"project three ",
:custom_fields=>
[{:id=>4, :name=>"Year", :value=>"2012"},
{:id=>5, :name=>"Priority", :value=>"high"}]}],
"2011"=>
[{:id=>2,
:name=>"project two ",
:custom_fields=>
[{:id=>4, :name=>"Year", :value=>"2011"},
{:id=>5, :name=>"Priority", :value=>"medium"}]}],
"2010"=>
[{:id=>1,
:name=>"project one ",
:custom_fields=>
[{:id=>4, :name=>"Year", :value=>"2010"},
{:id=>5, :name=>"Priority", :value=>"low"}]}]}
Assume projects is the data you pasted.
def order_values_by_id(pjs, id)
pjs.map{|p| p[:custom_fields].find{|f| f[:id] == id}[:value] }.sort.reverse
end
order_values_by_id(projects)
#=> ["2012", "2011", "2010"]

Get last occurence of nested array

By querying two classes and applying the following methods:
.each_with_index.chunk { |enum, i| enum.is_a?(Note) }
I get this structure:
[
[false, [[#<Post id: 1, title: "something", 0]]],
[false, [[#<Post id: 2, title: "something", 1], [#<Post id: 3, title: "something", 2]]],
[true, [[#<Note id: 1, title: "something", 3], [#<Note id: 77, title: "something", 4]]]
]
In the view I need to know when I'm on the last object, in this case index position 4, how do I do this?
In the view you could just do:
array.flatten.last
Which looks like this:
[1] pry(main)> [[false, [["1"]]], [false, [["2"], ["post 3"]]], [true, [["1"], ["note 77"]]]].flatten.last
=> "note 77"
Thanks Cary for the revision.

Resources