How to use selectall-hashref for all columns - perl

I would like to implement this subroutine using selectall_hashref:
sub query {
use SQL::Abstract;
my $sql = SQL::Abstract->new;
my ($table, $fields, $where) = #_;
my ($stmt, #bind) = $sql->select($table, $fields, $where);
my $sth = $dbh->prepare($stmt);
$sth->execute(#bind);
my #rows;
while(my #row = $sth->fetchrow_array() ) {
my %data;
#data{ #{$sth->{NAME}} } = #row;
push #rows, \%data;
}
return \#rows;
}
Unfortunately selectall_hashref requires a list of wanted columns. Is there a way to write something similar my first subroutine?
Obviously this doesn't work:
sub query {
return $dbh->selectall_hashref(shift, q/*/);
}
The expected output could be an array of hashes or an hash of hashes:
{ '1' => { column1 => 'foo', column2 => 'bar' },
'2' => { column1 => '...', column2 => '...' },
... }
or
[ { column1 => 'foo', column2 => 'bar' },
{ column1 => '...', column2 => '...' },
... ]

What you want is selectall_arrayref, not selectall_hashref. That does this exactly.
use DBI;
use Data::Printer;
my $dbh = DBI->connect('DBI:mysql:database=foo;', 'foo', 'bar');
my $foo = $dbh->selectall_arrayref(
'select * from foo',
{ Slice => {} }
);
p $foo
__END__
\ [
[0] {
id 1,
baz "",
},
[1] {
id 2,
baz "",
},
]

Related

Extract subset of XML with XML::Twig

I'm trying to use
XML::Twig
to extract a subset of an XML document so that I can convert it to CSV.
Here's a sample of my data
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Actions>
<Click>
<Field1>Data1</Field1>
<Field2>Data2</Field2>
</Click>
<Click>
<Field1>Data3</Field1>
<Field2>Data4</Field2>
</Click>
</Actions>
And here's an attempt at coding the desired outcome
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
use Text::CSV; # later
use Data::Dumper;
my $file = shift #ARGV or die "Need a file to process: $!";
my $twig = XML::Twig->new();
$twig->parsefile($file);
my $root = $twig->root;
my #data;
for my $node ( $twig->findnodes( '//Click/*' ) ) {
my $key = $node->name;
my $val = $node->text;
push #data, { $key => $val }
}
print Dumper \#data;
which gives
$VAR1 = [
{
'Field1' => 'Data1'
},
{
'Field2' => 'Data2'
},
{
'Field1' => 'Data3'
},
{
'Field2' => 'Data4'
}
];
What I'm looking to create is an array of hashes, if that's best
my #AoH = (
{ Field1 => 'Data1', Field2 => 'Data2' },
{ Field1 => 'Data3', Field2 => 'Data4' },
)
I'm not sure how to loop through the data to extract this.
You structure has two levels, so you need two levels of loops.
my #data;
for my $click_node ( $twig->findnodes( '/Actions/Click' ) ) {
my %click_data;
for my $child_node ( $click_node->findnodes( '*' ) ) {
my $key = $child_node->name;
my $val = $child_node->text;
$click_data{$key} = $val;
}
push #data, \%click_data;
}
local $Data::Dumper::Sortkeys = 1;
print(Dumper(\#data));
Output:
$VAR1 = [
{
'Field1' => 'Data1',
'Field2' => 'Data2'
},
{
'Field1' => 'Data3',
'Field2' => 'Data4'
}
];

executing a function within an array within a hash in perl

I have a Perl data structurte like so
%myhash = (
k1 => v1,
kArray => [
{
name => "anonymous hash",
...
},
\&funcThatReturnsHash,
{
name => "another anonymous hash",
...
}
]
);
Elsewhere I iterate through the list in kArray which contains a bunch of hashes. I would like to either process the actual hash OR the hash returned by the function.
foreach my $elem( #{myhash{kArray}} ) {
if (ref($elem) == "CODE") {
%thisHash = &$elem;
}
else {
%thisHash = %$elem;
}
...
}
However ref ($elem) is always scalar or undefined. I tried func, &func, \&func, \%{&func}, in %myhash to no effect.
how do I extract the hash within the function in the main body?
Apart from the code sample you give being invalid Perl, the main problems seem to be that you are using == to compare strings instead of eq, and you are assigning a hash reference to a hash variable %thishash. I assure you that ref $elem never returns SCALAR with the data you show
It would help you enormously if you followed the common advice to use strict and use warnings at the top of your code
This will work for you
for my $elem ( #{ $myhash{kArray} } ) {
my $this_hash;
if ( ref $elem eq 'CODE' ) {
$this_hash = $elem->();
}
else {
$this_hash = $elem;
}
# Do stuff with $this_hash
}
or you could just use a map like this
use strict;
use warnings;
use 5.010;
use Data::Dump;
my %myhash = (
k1 => v1,
kArray => [
{
name => "anonymous hash",
},
\&funcThatReturnsHash,
{
name => "another anonymous hash",
}
]
);
for my $hash ( map { ref eq 'CODE' ? $_->() : $_ } #{ $myhash{kArray} } ) {
say $hash->{name};
}
sub funcThatReturnsHash {
{ name => 'a third anonymous hash' };
}
output
anonymous hash
a third anonymous hash
another anonymous hash
If you turn on strict and warnings, you'll see that:
foreach my $elem(#{mynahs{kArray}}) {
Isn't valid. You need at the very least a $ before mynahs.
But given something like this - your approach works - here's an example using map to 'run' the code references:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
sub gimme_hash {
return { 'fish' => 'paste' };
}
my $stuff =
[ { 'anon1' => 'value' },
\&gimme_hash,
{ 'anon2' => 'anothervalue' }, ];
my $newstuff = [ map { ref $_ eq "CODE" ? $_->() : $_ } #$stuff ];
print Dumper $newstuff;
Turns that hash into:
$VAR1 = [
{
'anon1' => 'value'
},
{
'fish' => 'paste'
},
{
'anon2' => 'anothervalue'
}
];
But your approach does work:
foreach my $element ( #$stuff ) {
my %myhash;
if ( ref $element eq "CODE" ) {
%myhash = %{$element -> ()};
}
else {
%myhash = %$element;
}
print Dumper \%myhash;
}
Gives:
$VAR1 = {
'anon1' => 'value'
};
$VAR1 = {
'fish' => 'paste'
};
$VAR1 = {
'anon2' => 'anothervalue'
};

Perl - retrieve values from a hash of hashes

How do we retrieve the values of keys in a Hash of Hashes in Perl?
I tried to use the keys function. I wanted to remove the duplicates and then sort them, which i could
do using the uniq and sort functions. Am I missing anything?
#!/usr/bin/perl
use warnings;
use strict;
sub ids {
my ($data) = #_;
my #allID = keys %{$data};
my #unique = uniq #allID;
foreach ( #unique ) {
#allUniqueID = $_;
}
my #result = sort{$a<=>$b}(#allUniqueId);
return #result;
}
my $data = {
'first' => {
'second' => {
'third1' => [
{ id => 44, name => 'a', value => 'aa' },
{ id => 48, name => 'b', value => 'bb' },
{ id => 100, name => 'c', value => 'cc' }
],
id => 19
},
'third2' => [
{ id => 199, data => 'dd' },
{ id => 40, data => 'ee' },
{ id => 100, data => { name => 'f', value => 'ff' } }
],
id => 55
},
id => 1
};
# should print “1, 19, 40, 44, 48, 55, 100, 199”
print join(', ', ids($data)) . "\n";
I know it's incomplete, but I am not sure how to proceed. Any help would be appreciated.
This routine will recursively walk the data structure and pull out all of the values that correspond to a hash key id, without sorting the results or eliminating duplicates:
sub all_keys {
my $obj = shift;
if (ref $obj eq 'HASH') {
return map {
my $value = $obj->{$_};
$_ eq 'id' ? $value : ref $value ? all_keys($value) : ();
} keys %$obj;
} elsif (ref $obj eq 'ARRAY') {
return map all_keys($_), #$obj;
} else {
return;
}
}
To do the sorting/eliminating, just call it like:
my #ids = sort { $a <=> $b } uniq(all_ids($data));
(I assume the uniq routine is defined elsewhere.)
Here's my version of the recursive approach
use warnings;
use strict;
sub ids {
my ($data) = #_;
my #retval;
if (ref $data eq 'HASH') {
push #retval, $data->{id} if exists $data->{id};
push #retval, ids($_) for values %$data;
}
elsif (ref $data eq 'ARRAY') {
push #retval, ids($_) for #$data;
}
#retval;
}
my $data = {
'first' => {
'second' => {
'third1' => [
{ id => 44, name => 'a', value => 'aa' },
{ id => 48, name => 'b', value => 'bb' },
{ id => 100, name => 'c', value => 'cc' }
],
id => 19
},
'third2' => [
{ id => 199, data => 'dd' },
{ id => 40, data => 'ee' },
{ id => 100, data => { name => 'f', value => 'ff' } }
],
id => 55
},
id => 1
};
my #ids = sort { $a <=> $b } ids($data);
print join(', ', #ids), "\n";
output
1, 19, 40, 44, 48, 55, 100, 100, 199
Update
A large chunk of the code in the solution above is there to work out how to extract the list of values from a data reference. Recent versions of Perl have an experimental facility that allows you to use the values operator on both hashes and arrays, and also on references ro both, so if you're running version 14 or later of Perl 5 and are comfortable disabling experimental warnings, then you can write ids like this instead
use warnings;
use strict;
use 5.014;
sub ids {
my ($data) = #_;
return unless my $type = ref $data;
no warnings 'experimental';
if ( $type eq 'HASH' and exists $data->{id} ) {
$data->{id}, map ids($_), values $data;
}
else {
map ids($_), values $data;
}
}
The output is identical to that of the previous solution

Anomalous push behaviour under Catalyst MVC

I would expect the following code
my #array;
for my $rapport ( qw( value1 value2 value3 ) ) {
push #array, { key => $rapport };
}
to produce:
$VAR1 = [
{
'key' => 'value1'
},
{
'key' => 'value2'
},
{
'key' => 'value3'
}
];
However, running this code segment under Catalyst MVC I get:
$VAR1 = [
{
'key' => [ 'value', 'value2', 'value3' ]
},
];
Can someone please explain to me why?
EDIT: could anyone with the same issue please add an example? I cannot reproduce after some code changes, but as it has been upvoted 5 times I assume some other users have also experienced this issue?
This code example...
#!/usr/bin/perl
use Data::Dumper;
my #input = ( "var1", "var2", "var3" );
my #array;
for my $rapport ( #input ) {
push #array, { key => $rapport };
}
print Dumper( \#array );
exit;
produces ...
$VAR1 = [
{
'key' => 'var1'
},
{
'key' => 'var2'
},
{
'key' => 'var3'
}
];
But the following...
#!/usr/bin/perl
use Data::Dumper;
my #input = [ "var1", "var2", "var3" ]; # sometimes people forget to dereference their variables
my #array;
for my $rapport ( #input ) {
push #array, { key => $rapport };
}
print Dumper( \#array );
exit;
shows...
$VAR1 = [
{
'key' => [
'var1',
'var2',
'var3'
]
}
];
As you can see both examples loop through an array but the second one is an array, that was initialized with a reference value. Since in Catalyst you normally ship various values through your application via stash or similar constructs, you could check weather your array really contains scalar values : )

Perl adding Lines into a Multi-Dimensional Hash

Hello I want to split a Line and add the Values in to a multi dimensional Hash. This is how the Lines look like:
__DATA__
49839382;Test1;bgsae;npvxs
49839384;Test2;bgsae;npvxs
49839387;Test3;bgsae;npvxs
So what I am doing now is:
my %prefix = map { chomp; split ';' } <DATA>;
But now I can only access Test1 with:
print $prefix{"49839382"}
But how can I also add the bgsae to the Hash so I can access is with
$prefix{"49839382"}{"Test1"}
Thank you for your help.
What structure are you trying to build?
use Data::Dumper;
my %prefix = map { chomp (my #fields = split /;/); $fields[0] => { #fields[1 .. $#fields] } } <DATA>;
print Dumper \%prefix;
Output:
$VAR1 = {
'49839384' => {
'Test2' => 'bgsae',
'npvxs' => undef
},
'49839382' => {
'Test1' => 'bgsae',
'npvxs' => undef
},
'49839387' => {
'npvxs' => undef,
'Test3' => 'bgsae'
}
};
Or do you need a deeper hash?
my %prefix;
for (<DATA>) {
chomp;
my $ref = \%prefix;
for (split /;/) {
warn "[$_]";
$ref->{$_} = {};
$ref = $ref->{$_};
}
}
Returns:
$VAR1 = {
'49839384' => {
'Test2' => {
'bgsae' => {
'npvxs' => {}
}
}
},
'49839382' => {
'Test1' => {
'bgsae' => {
'npvxs' => {}
}
}
},
'49839387' => {
'Test3' => {
'bgsae' => {
'npvxs' => {}
}
}
}
};
I don't know what you need the data for, but at a guess you want something more like this.
It builds a hash of arrays, using the first field as the key for the data, and the remaining three in an array for the value. So you can access the test number as $data{'49839382'}[0] etc.
use strict;
use warnings;
my %data = map {
chomp;
my #fields = split /;/;
shift #fields => \#fields;
} <DATA>;
use Data::Dumper;
print Data::Dumper->Dump([\%data], ['*data']);
__DATA__
49839382;Test1;bgsae;npvxs
49839384;Test2;bgsae;npvxs
49839387;Test3;bgsae;npvxs
output
%data = (
'49839384' => [
'Test2',
'bgsae',
'npvxs'
],
'49839382' => [
'Test1',
'bgsae',
'npvxs'
],
'49839387' => [
'Test3',
'bgsae',
'npvxs'
]
);