Merge 2 array/Hash into 1 variable Perl - arrays

I have 2 variables that are returned on form submission. I need to merge the values into 1 Variable that I can use to create SQL later on.
How can I get results that is union of all the values:
i.e. location_setting = '409','405' and Status = '501', '137', '124'
Here is my current code, but spits only values from h2
use Data::Dumper;
my $h1 = { 'location_setting' => [ '409' ], 'status' => [ '501' ] };
my $h2 = { 'status' => [ '137', '124' ], 'location_setting' => ['405'], 'classification' => ['0']};
my $x = {%$h1, %$h2};
print Dumper $x;

use List::Util qw( uniq );
my %h =
map {
$_ => [
uniq
$h1->{$_} ? #{ $h1->{$_} } : (),
$h2->{$_} ? #{ $h2->{$_} } : (),
]
}
uniq
keys(%$h1), keys(%$h2);
If you have lots of hashes or a variable number of hashes,
use List::Util qw( uniq );
my #hashes = ( $h1, $h2, ... );
my %h =
map {
my $key = $_;
$key => [ uniq map #$_, grep $_, map $_->{$key}, #hashes ]
}
uniq map keys(%$_), #hashes;

use Data::Dumper;
use List::Util qw( uniq );
my $h1 = { 'location_setting' => [ '409' ], 'status' => [ '501' ] };
my $h2 = { 'status' => [ '137', '124' ], 'location_setting' => [], 'classification' => ['0']};
my %x;
foreach my $h1key (keys %{$h1}) {
push #{$x{$h1key}}, #{${$h1}{$h1key}};
}
foreach my $h2key (keys %{$h2}) {
push #{$x{$h2key}}, #{${$h2}{$h2key}};
}
#$_ = uniq #$_
for values(%x);
my $x = \%x;
print Dumper $x;

Related

Perl: Can you use more than two arrays in the map function?

I've been learning Perl as of late, and ran into an interesting problem. How would I go about using two arrays in map like so?
use warnings;
use strict;
use Data::Dumper;
my $names->{name} = ['Bill', 'Smith'];
my $cars->{model} = ['Honda', 'Toyota'];
my $obj = {
'Students' => [
map {
'Name' => $_,
'Model' => $_
}, #{$names->{name}}, #{$cars->{model}}
]
};
print Dumper $obj;
This will print, it's producing two too many objects than I would like.
$VAR1 = {
'Students' => [
{
'Model' => 'Bill',
'Name' => 'Bill'
},
{
'Model' => 'Smith',
'Name' => 'Smith'
},
{
'Model' => 'Honda',
'Name' => 'Honda'
},
{
'Model' => 'Toyota',
'Name' => 'Toyota'
}
]
};
What I'd like to do is have map work in a way that it'll produce these results
$VAR1 = {
'Students' => [
{
'Model' => 'Honda',
'Name' => 'Bill'
},
{
'Model' => 'Toyota',
'Name' => 'Smith'
}
]
};
use warnings;
use strict;
use Data::Dumper;
my $names->{name} = [ qw(Bill Smith) ];
my $cars->{model} = [ qw(Honda Toyota) ];
my $obj = {
Students => [
map {
{ Name => $names->{name}[$_], Model => $cars->{model}[$_] }
}
0 .. $#{$names->{name}}
]
};
print Dumper $obj;
The body of the map uses both arrays so you only need to supply indices to it.
For this both arrayrefs must be of same length and items to be paired at same indices.
The syntax $#$arrayref is for the last index in the $arrayref, and since here we have to also dereference first there's an extra pair of {}.
What you have dereferences both arrayrefs, to build a single flat list for the map, producing
my $obj = {
'Students' => [
map {
'Name' => $_,
'Model' => $_
}, qw(Bill Smith Honda Toyota);
]
};
First, this makes no sense:
my $names->{name} = ['Bill', 'Smith'];
my $cars->{model} = ['Honda', 'Toyota'];
I'll use
my #names = ( 'Bill', 'Smith' );
my #models = ( 'Honda', 'Toyota' );
We could iterate over the indexes.
map { +{ Name => $names[$_], Model => $cars[$_] } }
0..$#names
Alternatively, we could enlist zip.
use List::Util qw( zip );
map { +{ Name => $_->[0], Model => $_->[1] } }
zip \#names, \#models
This is probably better written as a while loop:
my #names = qw( Bill Smith );
my #models = qw( Honda Toyota );
my #students;
push #students, { 'name' => shift #names, 'model' => shift #models } while #names;

Convert array laravel

I use Laravel framework and I have an array:
[▼
"0.022" => "24.00000000"
"0.013" => "506.00000000"
"0.041" => "65.00000000"
]
Could you help me to convert it to new format like this:
[▼
0 => {▼
"xxx": 0.022
"yyy": 24.00000000
}
1 => {▼
"xxx": 0.013
"yyy": 506.00000000
}
2 => {▼
"xxx": 0.041
"yyy": 65.00000000
}
]
Thank you very much.
$inputArray=array(
"0.022" => "24.00000000"
"0.013" => "506.00000000"
"0.041" => "65.00000000"
);
$outputArray=array();
foreach($inputArray as $key=>$val)
{
$obj['xxx']= $key;
$obj['yyy']= $val;
array_push($outputArray,$obj)
}
echo $outputArray;
$array = [
"0.022" => "24.00000000",
"0.013" => "506.00000000",
"0.041" => "65.00000000"
];
$data=array();
foreach($array as $key=>$value)
{
$data[]= ['xxx'=>$key,'yyy'=>$value];
}
echo "<pre>";
echo(json_encode($data));
Based on your output, you want increment index as the key
$item = [
'0.022' => '24.00000000',
'0.013' => '506.00000000',
'0.041' => '65.00000000'
];
$output = [];
$count = 0;
foreach($item as $key => $value) {
$output[$count]['xxx'] = $key;
$output[$count]['yyy'] = $value;
$count++;
}
echo json_encode($output);

How to send hash of hash of array items to csv

Hash of Hash of values were printing correctly to csv but hash of hash of array values are messing up my csv file. Please find my code below which is not working.
< use strict;
use warnings;
use Data::Dumper;
my %student_hash;
my $student_report = "StudentReport.csv";
%student_hash = (
'RollNummber1' => {
'studentname' => 'Boris',
'address' => ['Vietnam',
'local'
],
'DOB' => '5june2000'
},
'RollNummber2' => {
'studentname' => 'John',
'address' => [
'4th/floor',
'Culverdown/Street',
'WestHam',
'UK.',
],
'DOB' => '2feb2000'
},
'RollNummber3' => {
'studentname' => 'Karen',
'DOB' => '5march2000'
}
);
print "StudentHash:".Dumper(\%student_hash);
open(my $fh, '>', $student_report) or die("Couldn't open ile");
print $fh "DOB \t,ADDRESS \t,NAME \t\n";
foreach my $key(keys %student_hash){
foreach my $secondkeys (keys %{$student_hash{$key}}){
if($secondkeys =~ /DOB || studentname/) {
print $fh "$student_hash{$key}{$secondkeys} \t,"; }
if($secondkeys =~ /address/) {
print $fh Dumper $student_hash{$key}{$secondkeys} };
}
print $fh "\n";
}
close($fh);>
If I comment out printing the array part then the code works fine.Can someone please suggest how to print the array elements into csv either in one cell or in multiple cells.Thanks
Use Text::CSV - you can either use print if you've got an array reference to print. (It handles quoting for you).
Or you can use column_names to set the column ordering, and print_hr to output a hash ref in that order.
It looks like you're not worried about RollNumber so you can cheat and do:
foreach my $student ( values %student_hash ) {
}
To iterate. I'm unclear what you're trying to do with address. But a simple join would probably do the trick there. (either on linefeed or whitespace, depending on the desired output).
#!/usr/bin/env perl
use strict;
use warnings;
use Text::CSV;
my %student_hash = (
'RollNummber1' => {
'studentname' => 'Boris',
'address' => [ 'Vietnam', 'local' ],
'DOB' => '5june2000'
},
'RollNummber2' => {
'studentname' => 'John',
'address' => [ '4th/floor', 'Culverdown/Street', 'WestHam', 'UK.', ],
'DOB' => '2feb2000'
},
'RollNummber3' => {
'studentname' => 'Karen',
'DOB' => '5march2000'
}
);
my $csv = Text::CSV->new( { sep_char => ',', eol => "\n" } );
$csv->column_names( 'studentname', 'DOB', 'address' );
foreach my $student ( values %student_hash ) {
if ( defined $student -> {address} ) {
$student -> {address} = join (" ", #{$student->{address}});
}
$csv->print_hr( \*STDOUT, $student );
}

Perl: Format a hash reference (e.g. output of Dumper) into a String

I have a hash reference that contains nested key/value pairs, hash references and/or array references.
I'd like to emulate the result of Data::Dumper into a string, but with:
Remove 'quotes' from keys.
Remove white spaces from structures (but not values)
Bonus: sort keys alphabetically.
Bonus Bonus: print key/value pairs first, followed by hash references, followed by array references.
For example:
#!/usr/bin/perl -w
use strict;
use warnings;
use Data::Dumper;
my $hash_ref = {
'private' => {
'locked' => 'FALSE',
'allowedAuth' => 'Digest'
},
'profile' => 'Default',
'id' => '123456',
'privacy' => 'FALSE',
'public' => [
{
'allowed' => 'FALSE',
'configured' => {
'profileId' => 'hello world'
},
'isDefault' => 'TRUE',
'maxSessions' => '3'
},
{
'isDefault' => 'FALSE',
'privateId' => 'foo#bar.com',
'maxSessions' => '3',
'allowed' => 'FALSE',
'implicit' => '1',
}
],
'indicator' => 'FALSE'
};
print STDERR Dumper ($hash_ref);
Ideally, I'd like the output to be:
my $str = "id=>'123456',indicator=>'FALSE',profile=>'Default',privacy=>'FALSE',private=>{allowedAuth=>'Digest',locked=>'FALSE'},public=>[{allowed=>'FALSE',configured=>{profileId=>'hello world'},isDefault=>'TRUE',maxSessions=>'3'},{allowed=>'FALSE',implicit=>'1',isDefault=>'FALSE',maxSessions=>'3',privateId=>'foo#bar.com'}]";
I've attempted a recursive function; however, I'm not sure how to get rid of the commas at the end (especially of hash references - for array ref I can use an index and check if it's the last one). Also, sorting keys seems too difficult.
sub recHash
{
my ($hash_ref) = #_;
my $response = "";
for my $k (keys %$hash_ref) {
my $v = $hash_ref->{$k};
if (ref($v) eq "HASH") {
$response .= "$k=>{" . recHash($v) . "}"; # recurse through the hash references.
}
elsif (ref($v) eq "ARRAY") {
$response .= "$k=>[";
# recurse through the array references.
foreach my $item (#$v) {
$response .= "{".recHash($item)."},";
}
$response .= "],";
return $response;
}
else {
$response .= "$k=>'$v',";
}
}
return $response;
}
print recHash($hash_ref);
My output is (which I think is flawed when I keep running it):
private=>{allowedAuth=>'Digest',locked=>'FALSE',}profile=>'Default',id=>'123456',indicator=>'FALSE',privacy=>'FALSE',public=>[{configured=>{profileId=>'hello world',}maxSessions=>'3',allowed=>'FALSE',isDefault=>'TRUE',},{allowed=>'FALSE',maxSessions=>'3',implicit=>'1',privateId=>'foo#bar.com',isDefault=>'FALSE',},],
Out-of-the-box $Data::Dumper::Indent and $Data::Dumper::Sortkeys values will get you most of the way there.
use Data::Dumper;
my $hash_ref = { ... };
$Data::Dumper::Indent = 0;
$Data::Dumper::Sortkeys = sub {
my ($hash) = #_;
my %refval = ('' => -3, 'HASH' => -2, 'ARRAY' => -1);
return [ sort {
# prefer ref(val) "" to "HASH" to "ARRAY" to anything else
$refval{ref $hash->{$a}} <=> $refval{ref $hash->{$b}}
# and then sort lexicographically
|| $a cmp $b
} keys %$hash ];
};
my $rec_hash = Dumper($hash_ref);
$rec_hash =~ s/'(\w+)' => /$1=>/g;
$rec_hash =~ s/^\$VAR1 = //;
print $rec_hash;
Result:
{id=>'123456',indicator=>'FALSE',privacy=>'FALSE',profile=>'Default',
private=>{allowedAuth=>'Digest',locked=>'FALSE'},public=>
[{allowed=>'FALSE',isDefault=>'TRUE',maxSessions=>'3',configured=>
{profileId=>'hello world'}},allowed=>'FALSE',implicit=>'1',
isDefault=>'FALSE',maxSessions=>'3',privateId=>'foo#bar.com'}]};
You just need to sort the keys as follows:
for my $k (sort keys %$hash_ref) {
You are getting different outputs at every run as the hash keys are accessed in random order.

Convert hash into csv file

I got a hash:
%hash = (
Honda.Blue => '10',
Honda.Red => '10',
Honda.Yellow => '60',
Ford.Blue => '20',
Ford.Red => '25',
Ford.Yellow => '26',
Toyota.Blue => '17',
Toyota.Red => '16',
Toyota.Yellow => '18',
);
Need to convert this hash into a csv file with the following headers (make,blue_volume,red_volume,yellow_volume) and fill it with data
#Make,blue_volume,red_volume,yellow_volume
#Honda,10,10,60
#Ford,20,25,26
#Toyota,17,16,18
loop over %hash
#array = split('.',$key);
$make=$array[0];
$line = "$make,$hash{'$make.Blue'},$hash{'$make.Red'},$hash{'$make.Yellow'}";
push(#lines,$line);
foreach (#lines)
{
open (LOG, '>>summary.csv');
print LOG "$_";
close (LOG);
}
Need help figuring out this code.
First step:
use strict; says:
Bareword "Honda" not allowed while "strict subs" in use at xx.pl line 4.
That is not an approved way of creating the hash. I suggest:
use strict;
use warnings;
my %hash = (
Honda => { Blue => '10', Red => '10', Yellow => '60' },
Ford => { Blue => '20', Red => '25', Yellow => '26' },
Toyota => { Blue => '17', Red => '16', Yellow => '18' },
);
Then, you should probably use Text::CSV. However, it is not all that hard to do output with simple manipulation. We can exploit the fact that you've asked for blue, red, yellow which happen to be in alphabetic order:
print "make,blue_volume, red_volume,yellow_volume\n";
foreach my $make (sort keys %hash)
{
print "$make";
foreach my $colour (sort keys %{$hash{$make}})
{
print ",$hash{$make}{$colour}";
}
print "\n";
}
For the sample hash, the output is:
make,blue_volume, red_volume,yellow_volume
Ford,20,25,26
Honda,10,10,60
Toyota,17,16,18
If there was any risk of needing to use quotes or anything else, I'd use Text::CSV.
A solution with List::MoreUtils.
#!/usr/bin/env perl
use warnings;
use 5.012;
use List::MoreUtils qw/first_index/;
use Text::CSV;
my $file_out = 'my_new_file.csv';
my %hash = (
'Honda.Blue' => '10',
'Honda.Red' => '10',
'Honda.Yellow' => '60',
'Ford.Blue' => '20',
'Ford.Red' => '25',
'Ford.Yellow' => '26',
'Toyota.Blue' => '17',
'Toyota.Red' => '16',
'Toyota.Yellow' => '18',
);
my #brands = qw( Honda Ford Toyota );
my #colors = qw( Blue Red Yellow );
my #array;
for my $key ( keys %hash ) {
my( $brand, $color ) = split /\./, $key, 2;
my $idx_1 = first_index { $_ eq $brand } #brands;
my $idx_2 = first_index { $_ eq $color } #colors;
$array[$idx_1][0] = $brand;
$array[$idx_1][$idx_2+1] = $hash{$key};
}
my $csv = Text::CSV->new ( { binary => 1, eol => $/, auto_diag => 2 } )
or die Text::CSV->error_diag();
my $col_names = [ qw( Make blue_volume red_volume yellow_volume ) ];
open my $fh, '>:encoding(UTF-8)', $file_out or die $!;
$csv->print ( $fh, $col_names );
$csv->print ( $fh, $_ ) for #array;
close $fh;
If you iterate over the hash and make a line for each key, you will have each make repeated three times; instead, create another hash with all the makes by looping over %hash, extracting the make, and setting $makes{$make} = 1. Then loop over that to produce your lines.
When you extract the make from the %hash key, use /\./ as the split pattern; split always uses a pattern, not a simple string (with one odd exception), and you don't want to split on every character, which is what split '.' would do (thanks, codaddict, for pointing this part out).
'$make.Blue' uses single quotes, so it won't interpolate the variable. Use "$make.Blue" instead.
Move the open and close to before and after the #lines loop, respectively. There's no reason to open the file for each line.
Don't forget a "\n" at the end of each line (unless you are using the -l flag or say instead of print).
Check out Text::CSV::Slurp. It will allow you to turn a hash into CSV and vice versa as well.
For those interested I was able to figure it out, thanks for everyone's help!
my %hash;
$hash{'aaa.biz'} = 'domainRegistered';
$hash{'aaa.com'} = 'domainRegistered';
$hash{'aaa.info'} = 'domainRegistered';
$hash{'aaa.net'} = 'domainRegistered';
$hash{'aaa.org'} = 'domainRegistered';
$hash{'bbb.biz'} = 'domainRegistered';
$hash{'bbb.com'} = 'domainRegistered';
$hash{'bbb.info'} = 'domainRegistered';
$hash{'bbb.org'} = 'domainRegistered';
$hash{'bbb.us'} = 'domainRegistered';
foreach $key (sort keys %hash)
{
#array=split("\\.",$key);
push (#names, $array[0]);
}
#Extract unique values and sort
my %seen = ();
my #result = grep { !$seen{$_}++ } #names;
#names = sort { $a <=> $b } #result;
foreach $element (#names)
{
foreach $key (sort keys %hash)
{
#array=split("\\.",$key);
if (#array [0] eq $element){push (#values, $hash{$key});}
}
$values = join(",",#values);
$line = "$element,$values";
undef #values;
push(#lines,$line);
}
print join("\n",#lines);
open (summary, '>>summary.csv');
print summary join("\n",#lines);
close (summary);

Resources