Convert array of hashes to json - perl

I want to convert an array of hashes that I create like this:
while(...)
{
...
push(#ranks, {id => $id, time => $time});
}
To JSON:
use JSON;
$j = new JSON;
print $j->encode_json({ranks => #ranks});
But it is outputting this:
{"ranks":{"time":"3","id":"tiago"},
"HASH(0x905bf70)":{"time":"10","id":"bla"}}
As you can see it isnt able to write on of the hashes and there's no array...
I would like to output a JSON string that looked like this:
{"ranks":[{"time":"3","id":"tiago"},
{"time":"40","id":"fhddhf"},
{"time":"10","id":"bla"}]}

All of these are the same:
ranks => #ranks
'ranks', #ranks
'ranks', $ranks[0], $ranks[1], $ranks[2]
ranks => $ranks[0], $ranks[1] => $ranks[2]
So you're creating a hash with two elements when you mean to create a hash with one element.
You tried to use an array as a hash value, but hash values can only be scalars. It is common, however, to use a reference to an array as a hash value since references are scalars, and this is what encode_json expects.
print $j->encode_json( { ranks => #ranks } );
should be
print $j->encode_json( { ranks => \#ranks } );

print $j->encode_json({ranks => #ranks});
should be:
print $j->encode_json({ranks => \#ranks});

Try passing the array as a reference.
to_json({ranks => \#ranks},{ascii => 1,pretty => 1});

Related

How to get the hash keys and values of a hash in perl mojo

I try to figure out how to select explicit key and values of a hash.
my $hash = query('select id, name, required from table')->hashes
My output when I dump is:
var1 = bless ([
{
'name' => value,
'id' => value,
'required' => value
}.....
])
What I want is to get the follow output:
var1 = bless ([
{
'required' => value
}...
])
After that I want to compare if the index == index of another array.
You removed the class name from bless, but I guess it's Mojo::Collection. Use it's map method to iterate over the elements:
my $required = $hash->map(sub { required => $_->{required} });
# Untested.
Also, using the name $hash for something that's a collection of hashes is confusing.
I don't understand your last sentence about the index. If you want to extract the $index-th element, you can use
my $hash = $required->to_array->[$index];
or to get directly the value
my $value = $required->to_array->[$index]{required};
Once you get a hash reference, from whatever means, you access the keys like this:
$hash->{required}; # etc.
See perlref for more information. Also read perldsc and perlreftut.

Perl: Array of hashes - Get the element with id in hash

I have a Perl data structure, loaded from a json, with Data::Dumper looking like this:
$VAR1 = {
'Stat' => [
{
'statCfgFile' => 'statcfg_0001.json',
'statid' => 1,
'status' => 'running',
'something' => 'other'
},
{
'statCfgFile' => 'statcfg_0002.json',
'statid' => 2,
'status' => 'running'
'something' => 'other'
}
]
}
From another dataset, I get a statid to read the hash, but the given id does not match the array key, therefore I need to go into deep, reading the statid property.
Of course, I could loop through the array, but with a large array list, that might hit performance.
Is there a way to directly access the array element by the statid stored in the hash?
As the data model is in my hands and currently in development:
Would it be better not to use an array, but also a hash with the statid as the naming element?
The short answer is no, you can't pull a value out of an array based on a criteria without iterating over the array. But there are ways to write it without using a loop.
Say the data structure is $data and you want to pull hashes where statid equals $id_to_find.
my #matching_hashes = grep {$_->{statid} = $id_to_find}} #{$data->{Stat}};
If only the first hash matching the ID is relevant, you could use the core module List::Util function first, which does the same thing as grep but only returns the first match instead of a list. It would be faster than grep because it stops iterating over the array as soon as it finds one match.
use List::Util 'first';
my $matching hash = first {$_->{statid} = $id_to_find}} #{$data->{Stat}};
As you indicated, using hash lookups is much faster than list operations. You could create a hash index of array adresses. Note this still requires you iterate over the array once.
# create the index;
my %index;
my #array = #{$data->{Stat}};
for my $address (0..$#array) { # "$#array" is the last element of #array
my $hash = $array[$address];
my $id = $hash->{statid};
$index{$id} = $address; # now you can use an ID to get the array address
}
# use the index
my $id_to_find = 42;
my $wanted_array_address = $index{$id_to_find};
my $matching_hash = $data->{Stat}->[$wanted_array_address];
If the statid field is unique then I suggest you use something more like this
{
1 => 'statcfg_0001.json',
2 => 'statcfg_0002.json',
}
The hash includes more data than shown. I extend the example
Then you need a hash instead of just a string value for each statid
{
1 => {
statCfgFile => 'statcfg_0001.json',
statid => 1,
status => 'running',
something => 'other',
},
2 => {
statCfgFile => 'statcfg_0002.json',
statid => 2,
status => 'running',
something => 'other',
},
}

How to merge two hashes returned from sub, in Perl

I am trying to merge two hashes. But they are return values from functions. How can I dereference the return values inline? I don't want to use extra variables such as my $pos = makePos();
use v5.8.8;
use strict;
use warnings;
sub _makePos
{
my $desc= {
pos50 => {unit => 'm', desc => 'position error' },
pos95 => {unit => 'm', desc => '95% position error' }
};
return $desc;
}
sub _makeVel
{
my $desc= {
vel50 => {unit => 'm/s', desc => 'velocity error' },
vel95 => {unit => 'm/s', desc => '95% velocity error' }
};
return $desc;
}
my $descs = {_makePos(), _makeVel()};
use Data::Dumper;
print Dumper($descs);
this prints only the hash returned from _makeVel. how does it work?
$VAR1 = {
'HASH(0x21ea4a0)' => {
'vel50' => {
'desc' => 'velocity error',
'unit' => 'm/s'
},
'vel95' => {
'unit' => 'm/s',
'desc' => '95% velocity error'
}
}
};
changing this line as
my $descs = {%{_makePos()}, %{_makeVel()}};
worked!
Actually, your original solution did print both of the hashes, but the first one was "stringified" as it was being used as the key of your hash. It's there as HASH(0x21ea4a0).
I see you have a solution, but it might be worth explaining what was going wrong and why your solution fixed it.
Your two subroutines don't return hashes but, rather, hash references. A hash reference is a scalar value that is, effectively, a pointer to a hash.
A hash is created from a list of values. Your code that creates your news hash (actually, once again, a hash reference) is this:
my $descs = {_makePos(), _makeVel()};
This is two scalar values. The first is used as a key in the new hash and the second is used as the associated value - hence the results you get from Data::Dumper.
What you actually want to do is to "dereference" your hashes and get back to the actual hashes. You can dereference a hash using the syntax %{ ... }, where the ... is any expression returning a hash reference. And that's what you've done in your solution. You dereference the hash references, which gives you a list of key/value pairs. The pairs from the two dereferenced hashes are then joined together in a single list which is used to create your new, combined, hash.
I should point out that there's a danger in this approach. If your two subroutines can ever return references to hashes that contain the same key, then only one version of that repeated key will appear in the combined hash.

in perl ,how to use variable value as hash element

I am new to Perl, and can't find the answer to the question in the Learning Perl book.
For example I have a array like:
my #loop=("op1_sel","op2_sel");
and two hash table as:
my %op1_sel=(
"bibuf","000",
"self","101"
);
my %op2_sel=(
"zero","1",
"temp","0"
);
Now I want to use variables in the loop to loop for the hash table for a particular key
for example:
foreach(#loop)
{
print ${$_}{"bibuf"} ;
}
But it seems not working, I know the ${$_} part is wrong, can anyone can tell me how
to fix this ?
Use nested hashes. Like this:
my %op;
# put a hash reference into hash, twice
$op{op1_sel} = \%op1_sel;
$op{op2_sel} = \%op2_sel;
# later ...
foreach (keys %op) {
print "bibuf of $_: $op{$_}->{bibuf}\n";
};
Or, long story short, just
my %op = (
op1_sel => {
foo => 1,
bar => 2,
# ...
},
op2_sel => {
# ...
},
};
The {} construct creates a reference to anonymous hash and is the standard way of handling nested data structures.
See also perldoc perldsc.
You can't refer to lexical (my) variables using the ${$foo} syntax. You could probably make it work if they were package variables, but this would not be the right way to go about it.
The right way to do it is using a nested data structure.
I can see two obvious ways of doing it. You could either make an array of op_sel containing the inner hashes directly, or create a hash of hashes, and then index into that.
So "array of hashes":
my #op_sels = (
{
bibuf => '000',
self => '101',
},
{
zero => '1',
temp => '0',
},
);
for my $op (#op_sels) {
print $$op{bibuf};
}
and "hash of hashes":
my %op_sels = (
1 => {
bibuf => '000',
self => '101',
},
2 => {
zero => '1',
temp => '0',
},
);
for my $op_key (sort keys %op_sels) {
print $op_sels{$op_key}{bibuf};
}
You can use eval for this.
foreach(#loop)
{
eval "\%var = \%$_";
print $var{"bibuf"} ;
}

Perl getting hash from reference to an array containing a reference

I have the following code that returns a reference to an array with one element. That element contains a reference to a hash. I would like to eliminate this double referencing and work either with a copy of the hash or directly on the hash itself so I can use the keys.
Here's the code:
my $extracted = $db_obj->extract('source_tag', $source_tag);
So $extracted contains a reference to an array. That array has a single element, a reference to a hash. The hash looks like this when I use Data::Dumper:
$VAR1 = \\[
{
'data_center' => 'qe76',
'description' => 'locator',
'abs_delta_dollar_percent' => undef,
'content_type' => 'Raw',
'source_tag' => 'hg9efx4',
'producer' => 'partner',
'id' => '15282',
'storage_type' => 'box',
'storage_path' => '/mnt/storage/2012'
}
];
I'd like to be able to directly access the hash so I can use the keys to grab the values. How do I remove this double referencing?
my %hash = %{ $db_obj->extract('source_tag', $source_tag)->[0] } ;
my $extracted = $db_obj->extract('source_tag', $source_tag) -> [0];
That seems to be:
$$$$result[0]
or
$$$result[0]
if you didn't mean that double backslash.