Perl: array and hash - arrays

Further to finding an answer to my earlier question Perl: slicing an array of hashes, I am stuck again and unable to see what I have done wrong.
What I have is
Array( Array0(Hash0,Hash1),Array1(Hash0,Hash1),Array2(Hash0,Hash1)...)
use strict;
use warnings;
my #DDs = ();
my #Ds = ();
my %hsh = ();
my %dot1 = ( 'x' => 1, 'y' => 2, 'r' => 3 );
my %dot2 = ( 'x' => 4, 'y' => 5, 'r' => 6 );
my %dot3 = ( 'x' => 7, 'y' => 8, 'r' => 9 );
my %dot4 = ( 'x' => 1.1, 'y' => 1.2, 'r' => 1.3 );
my %dot5 = ( 'x' => 2.1, 'y' => 2.2, 'r' => 2.3 );
my %dot6 = ( 'x' => 3.1, 'y' => 3.2, 'r' => 3.3 );
my %dot7 = ( 'x' => 4.1, 'y' => 4.2, 'r' => 4.3 );
my %dot8 = ( 'x' => 5.1, 'y' => 5.2, 'r' => 5.3 );
my #dotsA = ( \%dot1, \%dot2 );
my #dotsB = ( \%dot3, \%dot4 );
my #dotsC = ( \%dot5, \%dot6 );
my #dotsD = ( \%dot7, \%dot8 );
my %Ds = ( \#dotsA, \#dotsB, \#dotsC, \#dotsD );
#DDs = $Ds[1]; #expect #dotsB with scalar of 2
###"Can't use an undefined value as HASH reference" error here
%hsh = %{ $DDs[0] }; #expect %dot3
print scalar #DDs, "\n"; #expect 2 but has value of 1
print $hsh{'x'}, "\n";

Reference found where even-sized list expected at /Users/schwern/tmp/test.plx line 10.
Line 10 is this:
my %dot1 = {'x'=>1,'y'=>2,'r'=>3};
This is Perl's cryptic way of saying you fed a hash reference to a hash. Perl, unfortunately, distinguishes very strongly between things and references to that thing.
%dot1 is a hash. It takes a list and turns it into a hash. A list like ( x => 1, y => 2, r => 3). { x => 1, y => 2, r => 3 } creates a hash reference. That's a single thing, a scalar. It's like saying my %dot1 = (42). It doesn't make any sense.
%dot1 is a hash, it wants a list like (x => 1, y => 2)
$dot1 is a scalar, it can store a hash reference like { x => 1, y => 2 }.
my %Ds = (\#dotsA,\#dotsB,\#dotsC,\#dotsD);
A hash requires a key and a value, pairs. last_name => "Schwern". When you give it a bunch of array references like that, it will read them as key1, value1, key2, value2... but what is it using as the key? It's using the stringification of that reference, something like ARRAY(0x7fb721800468).
If you asked for $D{\#dotsA} you'll get back a reference to #dotsB. You will not be able to get #dotsA back, a Perl hash key is just a string, not a reference.
This isn't a very good way to store an array in a hash. I'm not sure what you're trying to accomplish, but you probably want to reference them by name.
# A hash of lists.
my %Ds = ( A => \#dotsA, B => \#dotsB, C => \#dotsC, D => \#dotsD );
# Get back a reference to #dotsA.
my $dotsA = $Ds{A};
But looking at the following code, #DDs = $Ds[1];, I think you meant to initialize #Ds instead of %Ds.
#Ds = (\#dotsA,\#dotsB,\#dotsC,\#dotsD);
And now the following works... sort of. More later.
#DDs = $Ds[1]; #expect #dotsB with scalar of 2
Unlike in PHP, hashes and arrays are totally different things. my #Ds and my %Ds declare totally different variables. It doesn't help that you access them both with $Ds. In Perl5, the sigil indicates what's going to get returned. $Ds[1] and $Ds{foo} both use $Ds because they're returning a scalar. #Ds[1,2] and #Ds{(foo, bar)} use #Ds because they're returning a list (known as a slice). Confusing, but that's how it works.
#DDs = $Ds[1]; #expect #dotsB with scalar of 2
You're not getting #dotsB, you're getting a reference to #dotsB. All complex data structures in Perl store references, not the actual value. This is like $DDs[0] = \#dotsB. If you want to get the actual value you have to dereference it.
#DDs = #{$Ds[1]}; # Now #DDs has a copy of #dotsB
And finally it works.
#!/usr/bin/perl
use strict;
use warnings;
use v5.10; # for say()
my %dot1 = ('x'=>1,'y'=>2,'r'=>3);
my %dot2 = ('x'=>4,'y'=>5,'r'=>6);
my %dot3 = ('x'=>7,'y'=>8,'r'=>9);
my %dot4 = ('x'=>1.1,'y'=>1.2,'r'=>1.3);
my %dot5 = ('x'=>2.1,'y'=>2.2,'r'=>2.3);
my %dot6 = ('x'=>3.1,'y'=>3.2,'r'=>3.3);
my %dot7 = ('x'=>4.1,'y'=>4.2,'r'=>4.3);
my %dot8 = ('x'=>5.1,'y'=>5.2,'r'=>5.3);
my #dotsA = (\%dot1,\%dot2);
my #dotsB = (\%dot3,\%dot4);
my #dotsC = (\%dot5,\%dot6);
my #dotsD = (\%dot7,\%dot8);
my #Ds = (\#dotsA,\#dotsB,\#dotsC,\#dotsD);
my #DDs = #{$Ds[1]}; #expect #dotsB
my %hsh = %{$DDs[0]}; #expect %dot3
say scalar #DDs; #expect 2
say $hsh{'x'};
I would also advise that you get comfortable working directly with references since that's what complex data structures are: references. Converting back and forth from references to values is confusing. Working with references is one less thing to convert in your code, in your head, and less copying done in your program.
#!/usr/bin/perl
use strict;
use warnings;
use v5.10; # for say()
my $dot1 = {'x'=>1,'y'=>2,'r'=>3};
my $dot2 = {'x'=>4,'y'=>5,'r'=>6};
my $dot3 = {'x'=>7,'y'=>8,'r'=>9};
my $dot4 = {'x'=>1.1,'y'=>1.2,'r'=>1.3};
my $dot5 = {'x'=>2.1,'y'=>2.2,'r'=>2.3};
my $dot6 = {'x'=>3.1,'y'=>3.2,'r'=>3.3};
my $dot7 = {'x'=>4.1,'y'=>4.2,'r'=>4.3};
my $dot8 = {'x'=>5.1,'y'=>5.2,'r'=>5.3};
my $dotsA = [$dot1,$dot2];
my $dotsB = [$dot3,$dot4];
my $dotsC = [$dot5,$dot6];
my $dotsD = [$dot7,$dot8];
my $Ds = [$dotsA,$dotsB,$dotsC,$dotsD];
my $DDs = $Ds->[1]; #expect $dotsB
my $hsh = $DDs->[0]; #expect $dot3
say scalar #$DDs; #expect 2
say $hsh->{'x'};
You should review perlreftut and the Nested Data Structures chapter of Modern Perl.

Related

Add an array to a hash entry that is already storing an array

I need to evaluate a series of strings that I want to convert into a hash of arrays. Take into account that, in this case, I want to add an array to an entry in a hash that it is already storing an array. I need to get the following hash:
ConfigurationHash{'Editor'} = (John, Mary, Jane, Peter)
I have stripped down my code to this example:
use strict;
use warnings;
my %ConfigurationHash;
my $String1 = "Editor=John,Mary";
my $String2 = "Editor=Jane,Peter";
my #Line1 = split ("=", $String1);
my #Line2 = split ("=", $String2);
my $Variable1 = #Line1[0];
my $Value1 = #Line1[1];
my $Variable2 = #Line2[0];
my $Value2 = #Line2[1];
my #Values1Array = split(",", $Value1);
my #Values2Array = split(",", $Value2);
if ( ! exists $ConfigurationHash{$Variable1} ) {
$ConfigurationHash{$Variable1} = #Values1Array;
}
else {
push (#ConfigurationHash{$Variable1}, #Values1Array);
}
which produces the following error:
Experimental push on scalar is now forbidden at ./test.pl line 25, near "#Values1Array)"
Execution of ./test.pl aborted due to compilation errors.
I know that the problem lies in references/dereferences, but my knowledge of perl is so basic that I'm not able to figure how to get there by myself.
Could anybody show me how to do it? I would also appreciate if you could show me how to iterate the values of the array in the hash once it is created.
It's unclear why you have $String2 and its derivatives in your code as they are never used. This code processes both strings
You simply need to push the list of values to the array corresponding to $Variable1 (dreadful choice of identifier) in the hash. Accomplish this by dereferencing the array element
use strict;
use warnings;
my %config;
my $s1 = 'Editor=John,Mary';
my $s2 = 'Editor=Jane,Peter';
for ( $s1, $s2 ) {
my ($key, #values) = split /[=,]/;
push #{ $config{$key} }, #values;
}
use Data::Dumper;
print Dumper \%config;
output
$VAR1 = {
'Editor' => [
'John',
'Mary',
'Jane',
'Peter'
]
};
This line isn't doing what you think it does.
$ConfigurationHash{$Variable1} = #Values1Array;
If you printed out what $ConfigurationHash{$Variable1} contains you'll find it only contains the size of #Values1Array.
You should be fine to use push but with a slight modification to what you've written.
push #{$ConfigurationHash{$Variable1}}, #Values1Array;
I've also removed the brackets as you don't need them.
As for iterating over the array, it is no different to iterating over a regular array. You were likely having problems iterating over it before as you didn't have an array
foreach my $whatever (#{$ConfigurationHash{$Variable1}})
{
# Code
}
Thank you to all who posted answers. #Borodin, you're right, I missed a second block that used $String2 and its derivatives, but I think it was obvious it was at the end and was similar to the if-else block in my original code.
Thank you, #chris-turner, for giving me the hint on how to use push the right way and pointing out the error in the $ConfigurationHash{$Variable1} = #Values1Array;
With all these contributions I figured out that the right code I was expecting is:
use strict;
use warnings;
my %ConfigurationHash;
my $String1 = "Editor=John,Mary";
my $String2 = "Editor=Jane,Peter";
my #Line1 = split ("=", $String1);
my #Line2 = split ("=", $String2);
my $Variable1 = $Line1[0];
my $Value1 = $Line1[1];
my $Variable2 = $Line2[0];
my $Value2 = $Line2[1];
my #Values1Array = split(",", $Value1);
my #Values2Array = split(",", $Value2);
if ( ! exists $ConfigurationHash{$Variable1} ) {
$ConfigurationHash{$Variable1} = \#Values1Array;
}
else {
#push (#ConfigurationHash{$Variable1}, #Values1Array);
push #{$ConfigurationHash{$Variable1}}, #Values1Array;
}
if ( ! exists $ConfigurationHash{$Variable2} ) {
$ConfigurationHash{$Variable2} = \#Values2Array;
}
else {
#push (#ConfigurationHash{$Variable2}, #Values2Array);
push #{$ConfigurationHash{$Variable2}}, #Values2Array;
}
use Data::Dumper;
print Dumper \%ConfigurationHash;
Which outputs the following:
$VAR1 = {
'Editor' => [
'John',
'Mary',
'Jane',
'Peter'
]
};

Cakephp Hash combine concatenate two fields

How can I concatenate two fields inside a select box using cakephp 2.5 and Hash:combine ?
Today I have name:
$banks = Hash::combine($banks, '{n}.Bank.id', '{n}.Bank.name');
I need name and identifier, I tried this:
$banks = Hash::combine($banks, '{n}.Bank.id', '{n}.Bank.name {n}.Bank.identifier');
But it return NULL.
How can I have : name - identifier ?
Also try to concatenate the two fields at the model but could not add an hiffen between name and identifier.
You can provide array’s for both $keyPath and $valuePath. If you do this, the first value will be used as a format string, for values extracted by the other paths:
$result = Hash::combine(
$a,
'{n}.User.id',
array('%s: %s', '{n}.User.Data.user', '{n}.User.Data.name'),
'{n}.User.group_id'
);
/* $result now looks like:
Array
(
[1] => Array
(
[2] => mariano.iglesias: Mariano Iglesias
)
[2] => Array
(
[14] => phpnut: Larry E. Masters
)
)
*/
$result = Hash::combine(
$a,
array('%s: %s', '{n}.User.Data.user', '{n}.User.Data.name'),
'{n}.User.id'
);
/* $result now looks like:
Array
(
[mariano.iglesias: Mariano Iglesias] => 2
[phpnut: Larry E. Masters] => 14
)
*/
See CookBook > Hash for mode details.

How to count the length of array within an object? [duplicate]

This question already has answers here:
Get the length of an array within a Perl hash
(3 answers)
Closed 7 years ago.
Here is my code:
#!/usr/bin/perl
use Net::Twitter;
use JSON;
use LWP::Simple;
use XML::Bare;
use Data::Dumper;
# keys for twitter
$consumer_key = "key";
$consumer_secret = "key";
$token = "key-key";
$token_secret = "key";
# As of 13-Aug-2010, Twitter requires OAuth for authenticated requests
my $nt = Net::Twitter->new(
traits => [qw/API::RESTv1_1/],
# traits => [qw/API::Search/],
consumer_key => $consumer_key,
consumer_secret => $consumer_secret,
access_token => $token,
access_token_secret => $token_secret,
);
$UserID = 46562311;
my $IDs = $nt->friends_ids({user_id => $UserID});
print Dumper $IDs->{ids};
The output is:
$VAR1 = [
1243478918,
472407213,
1152523333,
18080969,
36843988,
24467012,
322990625,
15492359,
2244340904,
22938914,
151180133,
58545637,
62513246,
28412286,
5746452,
17104751,
1636590253,
25073877,
8161232,
2384071,
13,
93957809,
22330739,
44196397,
132961448,
754500,
94384796,
59604041,
386854967,
15485441,
190617492,
226237599,
114485232,
190486752,
18645153,
326350862,
546915948,
1927794380,
244360712,
254674228,
43593,
17242168,
17268874,
117539435,
116737145,
326642243,
1010165784,
19583340,
7936522,
166739404,
50374439,
593893034,
1244526223,
783214,
382267114,
299743215,
16129880,
20536157,
17006157,
2249234689,
26761757,
17919972,
2244994945,
1526228120,
11348282,
14159148,
50393960,
1339835893,
409685881,
2469260288,
1012494116,
270816968,
813286,
2546660556,
518542127,
794611698,
108962082,
1626691956,
734314441,
20351217,
1692371666,
774980724,
865699670,
1958038938,
826539409,
805891104,
14749606,
391858548,
26693596
];
How do I count the length of this array? I used "length $IDs->{ids}" but it only gave me 21 which is obviously wrong.
You can use scalar builtin function:
print scalar #{ $IDs->{ids} };
It forces it's argument to be interpreted in scalar context. And as you know array in scalar context is evaluated to it's length.

Perl: using variable as an array element to divide major array into smaller arrays

I have a major array #major that I want to divide in a given number of minors arrays #minor , by setting the number of slices I want (let's say 4 here, but I wish to be able to chose whatever number I want), like this pseudo-code:
(I know it's not correct, but it sort of gives you the idea)
#major = A,B,C,D,E,F,G,H,I,J
$slice = 4;
$arraySize = $#array + 1;
$slicesize = $arraySize / $slice;
#minor1 = #major[0..$slicesize]
#minor2 = #major[($slicesize+1)..(2*$slicesize)]
#minor3 = #major[((2*$slicesize)+1)..(3*$slicesize)]
#minor4 = #major[((3*$slicesize)+1)..(4*$slicesize)]
The goal here is that I want to be able to change this size of the initial array #major and/or the number of slices $slice and that all values which set the size of the differents minor arrays (($slicesize+1), (2*$slicesize), and so on).
I know this looks a bit complex but I don't know to expose it in another way.
Do you have any idea about how to achieve this ?
I am not pretty sure you meant like this, but here it is how I understood your point:
#!/usr/bin/perl
use strict; use warnings; use 5.010;
my #array = ( 'A' .. 'Z' );
my $length_of_slice = $ARGV[0] || 5 ;
while ( #array ) {
local $" = ', ';
my #minor = splice #array, 0, $length_of_slice;
say "#minor";
}
When you have a complex data structure requirement, my first thought is - use an object. Perl supports object oriented programming, which allows you to do all manner of insanely complicated things like what you're trying to do.
It'd go something like this:
#!/usr/bin/perl
use strict;
use warnings;
package TestObject;
my #major = qw(A B C D E F G H I J );
sub new {
my ( $class, $slice_count ) = #_;
my $self = {};
if ( defined $slice_count ) { $self->{slice_count} = $slice_count }
$self->{major} = #major;
bless( $self, $class );
return $self;
}
sub get_slices {
my ( $self, $count ) = #_;
my #tmp = #major;
my #array_of_slices;
for ( 1 .. $count ) {
my $tmp_arr_ref = ();
for ( 0 .. ( #major / $count ) ) {
if ( #tmp ) { push( #$tmp_arr_ref, shift(#tmp) ) };
}
push( #array_of_slices, $tmp_arr_ref );
}
return (#array_of_slices);
}
Called by:
#!/usr/bin/perl
use strict;
use warnings;
use TestObject;
use Data::Dumper;
my $first = TestObject->new();
my #array_of_refs = $first->get_slices(4);
print Dumper \#array_of_refs;
And giving result of:
$VAR1 = [
[
'A',
'B',
'C'
],
[
'D',
'E',
'F'
],
[
'G',
'H',
'I'
],
[
'J'
]
];
Something like that (you'll have to adjust it a bit to get precisely what you have in mind depending on how you want to handle edge cases/rounding).

perl hash of hash of arrays with looping

I have the following data in a hash of hash of arrays. The numerical data in the array represents that last 4 quarters of financial information.
I'd like to be able to iterate over the array, and pull out the data by quarter, to get it ready to insert into a database. With my code below I can get all of the quarters, or one quarter only if I specifically call it. When I try and add another loop to iterate over the hash of hash of arrays to return only the array subset values, i get all the values and I don't know what I'm doing wrong. See code: Thanks for the help
my %comp_info = (
CompanyA => {
COGS => ["175.00", "155.00", "125.00", "125.00"],
Revenue => ["300.00", "200.00", "250.00", "225.00"],
},
)
# The following works, but I have to specifically push one array subset at a time,
# which makes passing values to db_insert subroutine ineffective.
# (Id have to have 4 or 5 calls depending on number of quarters of data in each record).
sub get_insert_arrays {
foreach $comp (keys %comp_info ) {
foreach $column ( keys %{$comp_info{$comp}} ) {
push (#insert_array, #{$comp_info{$sym}{$column}}[0] );
}
}
my $valuelist = join(", ", #insert_array);
&db_insert($valuelist);
undef #insert_array;
}
#try to loop through, fails, I get all of the data in #insert_array instead of only one quarter.
sub get_insert_arrays {
foreach $comp (keys %comp_info ) {
foreach $column ( keys %{$comp_info{$comp}} ) {
for my $i ( 0 .. $#{$comp_info{$comp}{$column}} ) {
push (#insert_array, #{$comp_info{$sym}{$column}}[$i] );
}
}
my $valuelist = join(", ", #insert_array);
&db_insert($valuelist);
undef #insert_array;
undef $valuelist;
}
}
I highly recommend to dereference with intermediate variables, and also using the -> syntax. Both of these help you figure out what's going on:
Here's your first subroutine using dereferencing:
sub get_insert_arrays {
my #insert_array;
foreach $comp (keys %comp_info ) { # References to a hash (Revenue, COGS)
%columns = %{ $comp_info{$comp} }; # Dereference
foreach $column ( keys %columns ) { # Reference to an Array (quarter data)
my #values = #{ $column } ; # Dereference
push (#insert_array, $column );
my $valuelist = join(", ", #insert_array);
&db_insert($valuelist);
}
}
}
Hmm... Looking at this, it's easy to see that I can simply do:
for my $comp (keys %comp_info ) { # References to a hash (Revenue, COGS)
%columns = %{ $comp_info{$comp} }; # Dereference
for my $column ( keys %columns ) { # Reference to an Array (quarter data)
my #values = #{ $column } ; # Dereference
db_insert(#values);
}
}
}
If you need to see a particular piece of data, use the -> syntax to simplify your structure:
${$comp_info{$sym}{$column}}[$i]; # You had "#". Should be "$".
vs.
$comp_info{$sym}->{$column}->[$i];
Much easier to read.
Also use the warnings and strict pragmas in your program. It'll catch a lot of errors that may include undefined variables, and misspelled variable names.
If you're pulling out data quarter-by-quarter, you probably want the COGS to be with the Revenue column:
#! /usr/bin/env perl
#
use strict; # Lets you know when you misspell variable names
use warnings; # Warns of issues (using undefined variables
use feature qw(say);
#
# Just initializing the data you had
#
my %comp_info = ( CompanyA => {
Revenue => [
300.00, 200.00, 250.00, 225.00
],
COGS => [
175.00, 155.00, 125.00, 125.00
],
},
);
#
# Go through each company
#
for my $company ( keys %comp_info ) {
my #revenues = #{ $comp_info{$company}->{Revenue} }; # Dereference
my #cogss = #{ $comp_info{$company}->{COGS} }; # Dereferenec
say "Company: $company";
#
# I know the keys are "Revenue" and "COGS", so I don't need a loop.
# I'll just go right to my quarters data. Note that dereferencing
# makes my program a lot easier to write and maintain
#
for my $quarter ( (0..3) ) {
my $revenue = $revenues[$quarter];
my $cogs = $cogss[$quarter];
my $profit = $revenue - $cogs;
say " Quarter: " . ($quarter - 1)
. " Revenue = $revenue COGS = $cogs Profit = $profit";
}
}
How you do your database inserts is up to you. But, you can see how doing a bit of dereferencing and using -> can clarify what you're looking at.
Addendum
how do I pull out just the quarter by quarter data without having to specify revenue, cogs, etc. in some cases there could be 30+ fields, so I don't want to have to specify each field in the program. I just want to grab all Q1 fields, insert, grab all Q2 field, insert, etc.
So two loops:
Like before, I have my outer loop go through for each company which just happens to be the key for that %comp_info hash.
Each value in the %comp_info hash is a reference to another hash that is keyed by the data type (COGS, Revenue, etc.). Again, I simply loop through the keys of that inner hash (after dereferencing to make it easier to understand).
Now that I have the company name (the key to that %comp_info hash, and a list of the keys in that inner hash, I can simply pull up the first quarter numbers for each company and each data type. Getting the quarter value is simple: $comp_info{$company}->{$type}->[$quarter]. Note that there is three levels of data, and I have three sections in my variable with each section separated by ->.
I can see that the outer most section is a simple hash that's keyed by the company name: ($comp_info{$company}).
This %comp_info hash points to a hash reference (->{type}) which is keyed by the data types (COGS, Revenue, etc.).
And that hash reference points to an array reference for each quarter (->[$quarter]). See how that works and why I like that -> syntax? It makes it very clear what I am working with.
This is just the first quarter results. If I wanted to go through each and every quarter, I could have an outer loop for my $quarter (0..3) {.
Here's what it looks like. This is a complete program, so you could cut this out, and try running it yourself and see if you can figure out what's going on.
use strict; # Lets you know when you misspell variable names
use warnings; # Warns of issues (using undefined variables
use feature qw(say);
my $quarter = 0; #First Quarter is 0. Last quarter is 3
my %comp_info = ( CompanyA => {
Revenue => [
300.00, 200.00, 250.00, 225.00
],
COGS => [
175.00, 155.00, 125.00, 125.00
],
},
);
for my $company ( keys %comp_info ) {
say "Company: $company";
%types = %{ $company_info{$company} };
for my $type ( keys %types ) { # COGS, Revenue, etc.
say " $type for quarter "
. ($quarter + 1) . ": "
. $comp_info{$company}->{$type}->[$quarter];
}
}
One more time for each quarter to insert the data into your database:
Using use strict and declaring variables with my means that variables are only valid for a limited scope. That my #type_data; declares a array that holds my type values for inserting into your database. However, since it's declared inside that for my $quarter loop, the array and its values disappears with each iteration of the loop. No need to have to remove the data or reinitialize the variable. It does it all by itself!
Look up how Lexically Scoped Variables work.
for my $quarter ( (0..3) ) {
my #type_values;
for my $company ( keys %comp_info ) {
my %types = %{ $comp_info{$company} };
for my $type ( keys %types ) { # COGS, Revenue, etc.
push #type_values, $comp_info->{$company}->{$type}->{quarter};
}
insert_data( #type_values ); # Database insert you wanted
}
Your recent addition - undef #insert_array; undef $valuelist; is a misuse of undef. Putting it before a variable like that forces a garbage collection cycle, which is something you don't want to do -- it is best to let Perl look after things itself.
Arrays should be emptied with #insert_array = () rather than using undef, and for scalars you should $valuelist = undef. But these variables are irrelevant outside the subroutine, so you should declare them inside, in which case there is no need to reinitialise them in the first place.
Please bear in mind what I said about calling prepare on an SQL statement with placeholders. Your code should look something like this
my $insert = $dbh->prepare('INSERT INTO table VALUES (?, ?)');
and later
my #insert_array = (175.00, 300.00);
$insert->execute(#insert_array);
However I have written this, which I think does what you want, to create a $valuelist string as your own code does. Since you don't need the hash keys, it is much tidier to iterate over the values instead. The db_insert subroutine is a dummy that just prints the values of the parameter passed to it.
use strict;
use warnings;
use 5.010;
my %comp_info = (
CompanyA => {
COGS => ["175.00", "155.00", "125.00", "125.00"],
Revenue => ["300.00", "200.00", "250.00", "225.00"],
},
);
my #values = map values %$_, values %comp_info;
for my $i (0 .. $#{$values[0]}) {
my #insert = map $_->[$i], #values;
db_insert(join ', ', #insert);
}
sub db_insert {
say "db_insert('#_')";
}
output
db_insert('175.00, 300.00')
db_insert('155.00, 200.00')
db_insert('125.00, 250.00')
db_insert('125.00, 225.00')
Update
To comply with the new specification:
use strict;
use warnings;
use 5.010;
my %comp_info = (
CompanyA => {
COGS => ["175.00", "155.00", "125.00", "125.00"],
Revenue => ["300.00", "200.00", "250.00", "225.00"],
},
CompanyB => {
COGS => ["175.00", "155.00", "125.00", "125.00"],
Revenue => ["300.00", "200.00", "250.00", "225.00"],
},
CompanyC => {
COGS => ["175.00", "155.00", "125.00", "125.00"],
Revenue => ["300.00", "200.00", "250.00", "225.00"],
},
);
my #columns = qw/ COGS Revenue /;
for my $comp (keys %comp_info) {
my $data = $comp_info{$comp};
for my $i (0 .. $#{(values %$data)[0]}) {
my #values = ( $comp, map $_->[$i], #{$data}{#columns} );
db_insert(join ', ', #values);
}
}
sub db_insert {
say "db_insert('#_')";
}
output
db_insert('CompanyC, 175.00, 300.00')
db_insert('CompanyC, 155.00, 200.00')
db_insert('CompanyC, 125.00, 250.00')
db_insert('CompanyC, 125.00, 225.00')
db_insert('CompanyA, 175.00, 300.00')
db_insert('CompanyA, 155.00, 200.00')
db_insert('CompanyA, 125.00, 250.00')
db_insert('CompanyA, 125.00, 225.00')
db_insert('CompanyB, 175.00, 300.00')
db_insert('CompanyB, 155.00, 200.00')
db_insert('CompanyB, 125.00, 250.00')
db_insert('CompanyB, 125.00, 225.00')

Resources