chaosflaws
chaosflaws

Reputation: 377

How do you treat hashes in arrays properly?

I've got an array of hashes:

my @questions = (
    {"Why do you study here?" => "bla"},
    {"What are your hobbies?" => "blabla"});

And I try to loop through it:

foreach (@questions) {
    my $key = (keys $_)[0];
    $content .= "\\section{$key}\n\n$_{$key}\n\n";
}

giving me

Use of uninitialized value in concatenation (.) or string at convert.pl line 44.

Where does the error come from?

Upvotes: 5

Views: 147

Answers (3)

ThisSuitIsBlackNot
ThisSuitIsBlackNot

Reputation: 24063

Gilles already explained how to use your current data structure, but I would recommend that you use a different data structure altogether: a simple hash.

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

my %answers = (
    "Why do you study here?" => "bla",
    "What are your hobbies?" => "blabla"
);

while (my ($question, $answer) = each %answers) {
    say "Question: $question";
    say "Answer: $answer";
}

Output:

Question: Why do you study here?
Answer: bla
Question: What are your hobbies?
Answer: blabla

I find this easier to work with than an array of hashes, each of which only contains a single key/value pair.

If you want to iterate through the hash in a certain (non-sorted) order, there are a couple of options. The simplistic solution is to maintain an array of keys in the order you want to access them:

# In the order you want to access them
my @questions = ("What are your hobbies?", "Why do you study here?");

my %answers;
@answers{@questions} = ("blabla", "bla");

foreach my $question (@questions) {
    say "Question: $question";
    say "Answer: $answers{$question}";
}

Output:

Question: What are your hobbies?
Answer: blabla
Question: Why do you study here?
Answer: bla

Another option is to use Tie::IxHash (or the faster XS module Tie::Hash::Indexed) to access keys in insertion order:

use Tie::IxHash;

tie my %answers, "Tie::IxHash";

%answers = (
    "Why do you study here?" => "bla",
    "What are your hobbies?" => "blabla"
);

while (my ($question, $answer) = each %answers) {
    say "Question: $question";
    say "Answer: $answer";
}

Output:

Question: Why do you study here?
Answer: bla
Question: What are your hobbies?
Answer: blabla

Upvotes: 2

Lee Duhem
Lee Duhem

Reputation: 15121

The elements of @questions are references to hash, not hashes. Therefore, you should use them like this:

foreach (@questions) {
    my $key = (keys %$_)[0];
    print "\\section{$key}\n\n$_->{$key}\n\n";
}

See perlref for how to create and use reference.

Upvotes: 2

$_{$key} looks up $key in the hash variable %_. The sigil $ at the beginning indicates that the type of the result is a scalar. It's the syntactic construct VAR{KEY} that determines that VAR must be a hash. Although $_ and %_ use the same symbol as a name, the different sigils make them unrelated variables.

You need to dereference the hash reference $_ into the underlying hash. The syntax for this is $_->{$key} or ${$_}{$key}.

See the reference tutorial for a more general presentation of the topic.

Upvotes: 3

Related Questions