Im trying to make a hashes of hashes to uniquely identify the number that only comes under one set of levels. the hash structure looks something like this :
my %gh = {
'Test1' => {
'level1a' => {
'level2b' => {
'level3a' => {
'level4a' => {
'level5' => '63'
}
}
}
}
}
};
Can some please tell me what is the simplest way to traverse the hash so i can get the value 63.
I have been using
my $x = '';
foreach my $l0 (%gh){
foreach my $l1 (%{$l0}){
foreach my $l2 (%$l1){
foreach my $l3 (%{$l2}){
foreach my $l4 (%$l3){
foreach my $l5 (%{$l4}){
$x = $l5;
}
}
}
}
}
}
This process seems to be working fine . But i was just looking for something simpler and shorter;
Thanks in advance
This will work in your case (only hashes, and plain scalar value at the end)
sub hval {
my ($h) = #_;
return map { ref() ? hval($_) : $_ } values %$h;
}
my $gh = {
'Test1' => {
'level1a' => {
'level2b' => {
'level3a' => {
'level4a' => {
'level5' => '63'
}
}
}
}
}
};
my ($x) = hval($gh);
If you use a reference to a hash instead, here is one way:
use warnings;
use strict;
my $gh = {
'Test1' => {
'level1a' => {
'level2b' => {
'level3a' => {
'level4a' => {
'level5' => '63'
}
}
}
}
}
};
print $gh->{Test1}{level1a}{level2b}{level3a}{level4a}{level5}, "\n";
See also: perldoc perldsc and Data::Diver
Related
I have a hash variable as a tree:
\%data = {
'node' => {
'RN:4' => {
'next' => {
'1' => {
'RN:23' => {
'next' => {
'1' => {
'RN:29' => {
'end' => 1
}
},
'2' => {
'RN:32' => {
'next' => {
'1' => {
'RN:30' => {
'end' = 1
}
}
}
}
}
}
I want to convert this tree to correct paths like this:
1, RN:4 >> RN:23 >> RN:29
2, RN:4 >> RN:23 >> RN:32 >> RN:30
I have tried some recursive code but alway get wrong path.
Help me please !
The data structure is wholly too complicated. Hashes are being used as arrays, and it would be easier if the id wasn't used as the key. It would be better if a node looked like this:
{
id => ...,
children => [ ... ]
}
The structure would become
[
{
id => 'RN:4',
children => [
{
id => 'RN:23',
children => [
{
id => 'RN:29',
children => []
},
{
id => 'RN:32',
children => [
{
id => 'RN:30',
children => []
}
]
}
]
}
]
}
]
You need the id of all ancestors so we pass a long a list of the ancestors as the parameters.
use 5.016;
sub print_paths {
my $i = 0;
my $helper = sub {
my $node = $_[-1];
my $children = $node->{children};
if (#$children) {
__SUB__->(#_, $_) for #$children;
} else {
say $i, ", ", join(" >> ", map { $_->{id} } #_);
}
};
$helper->(#_);
}
print_paths($_) for #$roots;
The above assumes the ends are the nodes with no children. If your ends can have children, you have a trie. Simply add end => 1 to the end nodes and use the following as the core of the visitor:
if (#$children) {
__SUB__->(#_, $_) for #$children;
}
if ($node->{end}) {
say $i, ", ", join(" >> ", map { $_->{id} } #_);
}
With your format, it's trickier (and more expensive).
$node->{id} is replaced with (keys(%$node))[0].
$node->{children} is replaced with $node->{$id}{next}.
$node->{end} is replaced with $node->{$id}{end}.
for my $child (#$children) is replaced with for (my $j=1; my $child = $children->{$j}; ++$j).
use 5.016;
sub print_paths {
my $i = 0;
my $helper = sub {
my $node = $_[-1];
my $id = (keys(%$node))[0];
my $children = $node->{$id}{next};
if ($children) {
for (my $j=1; my $child = $children->{$j}; ++$j) {
__SUB__->(#_, $child) for #$children;
}
}
if ($node->{$id}{end}) {
say $i, ", ", join(" >> ", map { (keys(%$node))[0] } #_);
}
};
$helper->(#_);
}
print_paths($data->{node});
I am having dictionary like this.
print Dumper($emp)
$VAR1 = {
'mike' => {
'country' => {
'US' => {
'pop' => 100
}
}
}
}
I want to append a new entry inside 'country' like this.
$VAR1 = {
'mike' => {
'country' => {
'US' => {
'pop' => 100
},
'Canada' => {
'pop' => 101
}
}
}
}
Right now I am building it like this
$emp -> {$name}{country} = getCountry();
sub getCountry{
....
return country;
}
It's not clear what getCountry returns. Seeing as it's a single scalar, I'm going to assume it's a hash of countries keyed by name despite the name.
{ Canada => { pop => 101 } }
A simple way to merge two hashes is
%h = ( %h, %new );
so
%{ $emp->{$name}{country} } = (
%{ $emp->{$name}{country} },
%{ getCountry() },
);
If getCountry were to return the country's name and the country, you'd could use the following:
my ($country_name, $country) = getCountry();
$emp->{$name}{country}{$country_name} = $country;
So, if the hash returned by getCountry returns just a single country, you could also do the following without changing getCountry:
my ($country_name, $country) = %{ getCountry() };
$emp->{$name}{country}{$country_name} = $country;
I'm writing a Perl module Result to manage HTTP errors.
I am a little lost to generate the function managing all this.
Result.pm
package Result;
use strict;
use warnings;
use Data::Dumper;
use Log::Log4perl;
sub new
{
my $class = shift;
my %params = #_;
my $self = {
code => $params{'code'},
value => $params{'value'}
};
bless $self, $class;
return $self;
}
sub generateError
{
my $self = shift;
my %params = #_;
if($self->{'code'} == 200)
{
return "Your request was well executed !";
}
elsif($self->{'code'} == 400)
{
return "There is an error in your request! Please verify it!";
}
elsif($self->{'code'} == 404)
{
return "We did not find for what you ask !"
}
elsif($self->{'code'} == 500)
{
return "Internal error of the server, please re-try later !";
}
elsif($self->{'code'} == 504)
{
return "Your request has set of time to be executed, please retry !";
}
}
1;
App.pm
sub template
{
my $self = shift;
my $query = $self->query;
my $id = $query->param('id');
my $session = $self->param('session');
my $profile = $session->param('profile');
my $Project = Project->newFromId($id);
if(!$Project or $Project eq 'NOT_FOUND')
{
return $self->redirect('?rm=notfound');
}
if(!$profile->{'uid'} or $Project->{'userId'} != $profile->{'uid'})
{
return $self->redirect('?rm=notfound');
}
my $mailContent = from_json($Project->{'mail'});
my $templateContent = formatTemplate($Project->{'template'});
my $infos = [
{
'ID' => $id,
'TEMPLATE' => $templateContent,
'MAILSUBJECT' => $mailContent->{'subject'},
'MAILBODY' => $mailContent->{'body'}
}
];
if($mailContent->{'type'} eq 'text')
{
$infos->[0]{'MAILTEXT'} = 1;
}
else
{
$infos->[0]{'MAILHTML'} = 1;
}
return $self->processtmpl('template.tmpl', $infos);
}
1;
In some cases you may want to pass in an argument to a macro which is either some text, or nothing (blank space, as if nothing was written).
Given this starting point:
macro_rules! testme {
($var:ident, $code:block) => {
for i in 0..10 {
let $var = i;
{ $code }
if $var > 5 {
println!("over 5");
}
}
}
}
fn main() {
testme!(myvar, {
println!("{}", myvar);
});
}
We may want var to optionally be mutable, assuming the macro body is larger then in the example above, its best not to duplicate the entire macro.
macro_rules! testme {
(private $var:ident, $code:block, $var_qual:tt) => {
for i in 0..10 {
// imagine this is a lot more code :)
let $var_qual $var = i;
{ $code }
if $var > 5 {
println!("over 5");
}
}
};
(mut $var:ident, $code:block) => {
testme!(private $var, $code, mut)
};
/*
($var:ident, $code:block) => {
testme!(private $var, $code, )
// ^ how to pass in a blank argument?
};
*/
}
fn main() {
testme!(mut myvar_mut, {
myvar_mut += 10;
println!("{}", myvar_mut);
});
/*
testme!(myvar_immutable, {
println!("{}", myvar_immutable);
});
*/
}
As far as I can tell there is no way to pass in a an empty argument, uncomment the /**/ comments to see the error.
Is it possible to pass in an empty argument to a macro to make an example like this work?
As far as I know its not possible to pass in blank / empty arguments.
It is possible however to pass in a locally defined macro which optionally adds a prefix.
Working example:
macro_rules! testme {
(private $var:ident, $code:block, $var_qual_macro:ident) => {
for i in 0..10 {
// imagine this is a lot more code :)
let $var_qual_macro!($var) = i;
{ $code }
if $var > 5 {
println!("over 5");
}
}
};
(mut $var:ident, $code:block) => {
macro_rules! var_qualifier { ($v:ident) => { mut $v } }
testme!(private $var, $code, var_qualifier)
};
($var:ident, $code:block) => {
macro_rules! var_qualifier { ($v:ident) => { $v } }
testme!(private $var, $code, var_qualifier)
};
}
fn main() {
testme!(mut myvar_mut, {
myvar_mut += 10;
println!("{}", myvar_mut);
});
testme!(myvar_immutable, {
println!("{}", myvar_immutable);
});
}
Take care when nesting macros like this that the name of the macro (var_qualifier in this case) is isn't the same name used inside a different macro since the name will be silently shadowed.
I am trying to return today's birthdays. This is what I have right now, which works, but I need to grab the month and day to input into the statement. I thought maybe I could grab them from localtime, but that didn't work out. Any suggestions would be appreciated.
sub author_birth {
my ($self) = #_;
my ($day,$month) = (localtime())[3..4];
my $author_result = $self->search_like(
{
birth => '%03-20'
},
{
select => [
'id',
'complete_name',
],
#result_class => 'DBIx::Class::ResultClass::HashRefInflator'
}
);
my #author_ids = ();
while (my $row = $author_result->next) {
push #author_ids, $row->id;
}
return $self->get_author_info_by_id(\#author_ids);
}
I ended up doing something like this.
my ($self) = #_;
my $conc = '%';
my $datetime = Time::Piece->new->strftime('%m-%d');
my $date = $conc . $datetime;
my $author_result = $self->search_like(
{
birth => $date,
},
{
select => [
'id',
'complete_name',
],
#result_class => 'DBIx::Class::ResultClass::HashRefInflator'
}
);