1

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.

5 Answers 5

4

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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;

Comments

1

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).

Comments

1

Check out Text::CSV::Slurp. It will allow you to turn a hash into CSV and vice versa as well.

2 Comments

In this case, the hash needs processing to get the data to go into the CSV file; Text::CSV::Slurp won't do that for you.
@ysth - That doesn't mean it isn't still a viable option though.
0

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); 

1 Comment

Very curious; this has almost nothing in common with the question. The names Honda, Ford, Toyota do not appear in the answer; domain names do not appear in the question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.