+4  A: 

You want to iterate over

keys %inventory

and not

%inventory

which, as you see, causes you to iterate over key, value pairs.

Jonathan Feinberg
+3  A: 

You're using your hash in list context, so you're getting all your values thrown in with your keys. I think what you actually want to do is:

foreach $key (keys %inventory) {
    print FILE "...";
}
Jefromi
+2  A: 

EDIT: I was wrong about having to use an explicit dereferencing arrow; this is inferred between brackets when necessary, even if the first brackets do NOT require a dereference. That said, I will leave the remainder of the answer as posted since it was accepted, but merely note that if you choose not to use join, you needn't actually use $inventory{$key}->[0] but can in fact use $inventory{$key}[0] as originally posted.

Just be aware that the first (hash) brackets do not imply a dereference but the second (array) brackets do. Your errant array refs in the output were coming from looping over not only keys but also values of the hash.

ORIGINAL ANSWER:

In addition to using keys, you also need to dereference the references-to-array (this is why you're seeing each value output as ARRAY with an address---you're printing the references, not the values of the dereferenced array) when you print, so your loop becomes something like:

foreach my $key (sort keys %inventory) {
    print FILE "$key\|$inventory{$key}->[0]\|$inventory{$key}->[1]\|$inventory{$key}->[2]\n";
}

I'd probably rewrite it a little more idiomatically as:

foreach my $key (sort keys %inventory) {
    print FILE (join '|', $key, @{$inventory{$key}}) . "\n";
}

Hope that helps!

Derrick Turk
Your answer was the most helpful, thanks :D
Harm De Weirdt
@Harm: you're welcome! Please do read my edit though, I consulted `perldoc perlref` and it turned out I was overcautious about dereferencing.
Derrick Turk
There was actually a question a few days ago about whether the extra dereferencing arrows were good style: http://stackoverflow.com/questions/2475042/nested-dereferencing-arrows-in-perl-to-omit-or-not-to-omit
Jefromi
@Jefromi: interesting discussion; thanks!
Derrick Turk
+2  A: 

Here is one way to write that routine:

#!/usr/bin/perl

use strict; use warnings;

my %inventory;

while ( my $line = <DATA> ) {
    chomp $line;
    my ($key, @values) = split qr{\|}, $line;
    last unless @values;
    $inventory{$key} = \@values;
}

write_inventory(\%inventory, 'test.txt');

sub write_inventory {
    my ($inventory, $output_file) = @_;

    open my $output, '>', $output_file
        or die "Cannot open '$output_file': $!";

    for my $item ( keys %$inventory ) {
        unless ( 'ARRAY' eq ref $inventory{$item} ) {
            warn "Invalid item '$item' in inventory\n";
            next;
        }

        print $output join('|', $item, @{ $inventory{$item} }), "\n";
    }

    close $output
        or die "Cannot close '$output': $!";
}
__DATA__
CD-911|Lady Gaga - The Fame|15.99|21
BOOK-1453|The Da Vinci Code - Dan Brown|14.75|12

The rules are:

  • Don't use global variables: Pass a reference to %inventory to write_inventory instead of having it operate on the global %inventory.

  • Don't use global variables: Instead of using the bareword file handle FILE which has package scope, use a lexical file handle whose scope is limited to write_inventory.

  • Check for errors on file operations: Make sure open succeeded before plowing ahead and trying to write. Make sure close succeeded before assuming all data you printed actually got saved.

  • You MUST use strict and warnings because, at this point in your learning process, you do not know enough to know what you do not know.o

Sinan Ünür