Ruby nested hash syntax & structure - ruby

I'm building a tree of html elements, class names and their counts.
How would I structure this code with the proper syntax?
$html = {
:p => [
{ 'quote' => 10 },
{ 'important' => 4 }
],
:h2 => [
{ 'title' => 33 },
{ 'subtitle' => 15 }
]
}
I'm confused by the nested hash syntax. Thanks for the help setting me straight.

A simple way to structure a HTML tree could be:
html = [
{ _tag: :p, quote: 10, important: 4 },
{ _tag: :h2, title: 33, subtitle: 15 },
]
Where html[0][:_tag] is the tag name, and other attributes are accessible through html[0][attr]. The root element is an array since multiple elements of the same type (multiple paragraphs) could exist in the same namespace and a hash would only store the last added one.
A more advanced example which would allow nested contents:
tree = { _tag: :html, _contents: [
{ _tag: :head, _contents: [
{ _tag: :title, _contents: "The page title" },
]},
{ _tag: :body, id: 'body-id', _contents: [
{ _tag: :a, href: 'http://google.com', id: 'google-link', _contents: "A link" },
]},
]}

After defining the HTML element you don't assign another hash, but a list and from your question title I guess you want to nest another hash directly. Thus you do not start with a square bracket, but with another curly brace:
$html = {
:p => { 'quote' => 10, 'important' => 4 },
:h2 => { 'title' => 33, 'subtitle' => 15 }
}
#Example
puts $html[:p]['quote']
Which will print:
10
Take a look at the constructor documentation of Hash, there are different ways to initialize hashes, maybe you find a more intuitive one.

Related

Sorting a reference to an array of rows where each row is stored as a hash

I'm trying to sort the following data structure in Perl, by location_id.
my $employees = $dbh->selectall_arrayref(qq[
SELECT name, type, code, emp_cat_id,
percentage, location_id
FROM table_1
],{ Slice => {} });
for my $row (#$employees) {
push #{
$args->{employees}{ $row->{emp_cat_id} }
}, $row;
}
Example:
123 => [
{
percentage => 0.25,
code => "XYZ",
name => "John Doe",
type => "pt",
location_id => 001,
emp_cat_id => 123
}
],
555 => [
{
percentage => 0.50,
code => "ZZZ"
name => "Chris Cringle",
type => "ft",
location_id => 007,
emp_cat_id => 555
},
{
percentage => 0.25,
code => "XXX"
name => "Tom Thompson",
type => "pt",
location_id => 002,
emp_cat_id => 555
}
]
For every emp_cat_id, I need the structure to have the location_ids in asc order.
I've tried the following, but I get "useless use of sort in void context at line #" or "useless use of sort in scalar context at line #" errors.
$args->{employees} = sort {
$a->{location_id} <=> $b->{location_id}
} $args->{employees};
Any help understanding the sort is appreciated!
The problem is that you are sorting the array(ref) at emp_cat_id of 555, then of 123, and so need to dereference for sorting those arrayrefs. So
foreach my $id (keys $args->{employees}) {
#{ $args->{employees}{$id} } = sort {
$a->{location_id} <=> $b->{location_id}
}
#{ $args->{employees}{$id} }
}
(tested with the structure shown in the question, omitted here)†
Doing anything like this loses 007 into 7. This is of course possible to fix, let me know if it matters.
If you really have only the key employees then consider extracting the $args->{employees} hashref and working with that. It'll be way easier
use Storable qw(dclone);
my $employees = dclone $args->{employees}; # need deep copy
† Oh well, here's the whole thing
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd);
my $args = {
employees => {
123 => [
{
percentage => 0.25,
code => "XYZ",
name => "John Doe",
type => "pt",
location_id => 001,
emp_cat_id => 123
}
],
555 => [
{
percentage => 0.50,
code => "ZZZ",
name => "Chris Cringle",
type => "ft",
location_id => 007,
emp_cat_id => 555
},
{
percentage => 0.25,
code => "XXX",
name => "Tom Thompson",
type => "pt",
location_id => 002,
emp_cat_id => 555
}
]
}
};
foreach my $id (keys $args->{employees}) {
#{ $args->{employees}{$id} } = sort {
$a->{location_id} <=> $b->{location_id}
}
#{ $args->{employees}{$id} }
}
dd $args;
So, you have a hashref where each element is an arrayref of hashrefs that should be sorted based on a key of that inside hashref?
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my $hashref = {
123 => [
{
percentage => 0.25,
code => "XYZ",
name => "John Doe",
type => "pt",
location_id => 001,
emp_cat_id => 123
}
],
555 => [
{
percentage => 0.50,
code => "ZZZ",
name => "Chris Cringle",
type => "ft",
location_id => 007,
emp_cat_id => 555
},
{
percentage => 0.25,
code => "XXX",
name => "Tom Thompson",
type => "pt",
location_id => 002,
emp_cat_id => 555
}
]
};
foreach my $arrayref (values %$hashref) {
#$arrayref = sort { $a->{location_id} <=> $b->{location_id} } #$arrayref;
}
print Dumper($hashref);
The important part you're missing is in dereferencing the arrayrefs. #$arrayref instead of just $arrayref.

In Ruby, what's the advantage of #each_pair over #each when iterating through a hash?

Let's say I want to access the values of a hash like this:
munsters = {
"Herman" => { "age" => 32, "gender" => "male" },
"Lily" => { "age" => 30, "gender" => "female" },
"Grandpa" => { "age" => 402, "gender" => "male" },
"Eddie" => { "age" => 10, "gender" => "male" },
"Marilyn" => { "age" => 23, "gender" => "female"}
}
I could use #each with two parameters:
munsters.each do |key, value|
puts "#{name} is a #{value["age"]}-year-old #{value["gender"]}."
end
Or I could use #each_pair with two parameters:
munsters.each_pair do |key, value|
puts "#{name} is a #{value["age"]}-year-old #{value["gender"]}."
end
Perhaps the difference between the two is not borne out in this simple example, but can someone help me to understand the advantage of using #each_pair over #each ?
Because Hash is an Enumerable, it has to have an each method. each_pair may be a clearer name, since it strongly suggests that two-element arrays containing key-value pairs are passed to the block.
They are aliases for each other: they share the same source code.

What is a good way to sort an array by attribute that is not naturally ordered in Ruby?

array = [{ name:'Joe', class:'foo' },
{ name:'Bob', class:'bar' },
{ name:'Hal', class:'baz' },
{ name:'Kim', class:'qux' },
{ name:'Zoe', class:'bar' }
]
What is a good way to sort by class in the following order: qux, bar, foo, baz?
Like this, for example:
array = [{ name:'Joe', class:'foo' },
{ name:'Bob', class:'bar' },
{ name:'Hal', class:'baz' },
{ name:'Kim', class:'qux' },
{ name:'Zoe', class:'bar' }
]
order = %w[qux bar foo baz]
sorted = array.sort_by{|el| order.index(el[:class])}
sorted # => [{:name=>"Kim", :class=>"qux"},
# {:name=>"Bob", :class=>"bar"},
# {:name=>"Zoe", :class=>"bar"},
# {:name=>"Joe", :class=>"foo"},
# {:name=>"Hal", :class=>"baz"}]
order = %w[qux bar foo baz]
array.sort_by{|h| order.index(h[:class])}
gives:
[
{
:name => "Kim",
:class => "qux"
},
{
:name => "Bob",
:class => "bar"
},
{
:name => "Zoe",
:class => "bar"
},
{
:name => "Joe",
:class => "foo"
},
{
:name => "Hal",
:class => "baz"
}
]

Parse Array hashes in new object with ruby

I am struggling with some arrays with hashes inside. I want to parse them into a new object but have no idea how to do this.
Here is the data:
[
{
"name" => "itemHref",
"value" => "https://192.168.75.145:8281/api/workflows/16da1fa1-7c8b-4602-8d53-17fc5e1fa3ff/"
},
{
"name" => "id",
"value" => "16da1fa1-7c8b-4602-8d53-17fc5e1fa3ff"
},
{
"name" => "categoryName",
"value" => "FinanzInformatik"
},
{
"name" => "canExecute",
"value" => "true"
},
{
"name" => "categoryHref",
"value" => "https://192.168.75.145:8281/api/catalog/System/WorkflowCategory/ff8080813b90a145013b90cac51b0006/"
},
{
"name" => "description",
"value" => "bekommt alle VMs"
},
{
"name" => "name",
"value" => "getAllVms"
},
{
"name" => "type",
"value" => "Workflow"
},
{
"name" => "canEdit",
"value" => "true"
}
]
And, here is my code:
require 'rest-client'
require 'json'
class Workflow
def initialize(itemHref, id, categoryName, canExecute, categoryHref, description, name, type, canEdit)
#itemHref = itemHref
#id = id
#categoryName = categoryName
#canExecute = canExecute
#categoryHref = categoryHref
#description = description
#name = name
#type = type
#canEdit = canEdit
end
end
json_string = RestClient.get( "http://vcoadmin:vcoadmin#192.168.75.145:8280/api/workflows", :content_type => 'application/json', :accept => 'application/json')
parsed = JSON.parse(json_string)
parsed.each do |a, b|
if(b.class == Array)
b.flatten.each do |c|
p c['attributes']
#c['attributes'].each
{
|f| p f['name'], f['value'] }
end
end
end
How do I put the hash value into the object? I think about something based on the 'name' which is the identifier for the value.
Any ideas?
Assuming that the order of attributes shouldn't be changed:
Workflow.new(*parsed.map {|attr| attr['value']})
I would implement a PORO that can be initialized with a hash. So then you are able to pass your hash directly in to creating the workflow.
An example of this is can be seen: http://pullmonkey.com/2008/01/06/convert-a-ruby-hash-into-a-class-object/

How to render json with arbitrary keys using mustache?

I have a JSON object that looks like the following:
{
"XXX":{"name":"First"},
"YYY":{"name":"Second"},
....
}
I need to render it to look like:
<div>
<h1>XXX</h1>
<p>First</p>
</div>
<div>
<h1>YYY</h1>
<p>Second</p>
</div>
....
How will I accomplish this using Mustache? The problem I am facing is that I don't know how to reference the items because the key names are arbitrary.
Convert the JSON to a hash using your favorite JSON parser, that will give you something that looks like this:
json = {
"XXX" => {"name" => "First"},
"YYY" => {"name" => "Second"},
"ZZZ" => {"name" => "Third"}
}
Then simply rearrange it into a list of little hashes with known keys:
for_mustache = json.keys.inject([ ]) do |a, k|
a.push({ :k => k, :v => json[k]['name']})
a
end
There are probably cleverer ways to do that above. Now you'll have something simple and regular like this in for_mustache:
[
{ :k => "XXX", :v => "First" },
{ :k => "YYY", :v => "Second" },
{ :k => "ZZZ", :v => "Third" }
]
Then you can handle that data structure just like any other array of hashes in Mustache:
{{#for_mustache}}
<div>
<h1>{{k}}</h1>
<p>{{v}}</p>
</div>
{{/for_mustache}}

Resources