Reputation: 21
I'm working on a project introducing us to anonymous hashes. The assignment asks us to use an anonymous hash as the base object and then add the key value 'pizza_toppings', an array with at least 5 elements; and 'movies', an array that contains at least 3 hash objects who's keys must be 'title' and 'genre'. There's also a bit about modifying the pizza, which I've achieved.
What I'm stuck on, is I'm not sure how to access individual hash values when the have the same key. My goal is to clean up the output so I need to be able to cut off the loop before the last entry, add 'and' and then print the last one so that it looks like this:
My ideal pizza has pineapple, mushroom, onion, tomato, cheese, and green peppers.
I like to watch SciFi, Action, and Fantasy movies.
Some of my favourites are Arrival, Six Underground, and Practical Magic!
This is what I have so far. It works, but just isn't formatted properly. I'd also be interested to hear any better solutions to printing the genres and titles then what I did. I'm still a baby scripter! We didn't really get any explanation and I haven't been able to find how to access something a few levels in like this.
use 5.26.1;
use strict;
use warnings;
my %hash = (
'pizza_toppings' => ['pineapple', 'mushroom', 'onion', 'tomato', 'cheese'],
'movies' => [
{'genre' => 'SciFi', 'title' => 'Arrival'},
{'genre' => 'Action', 'title' => 'Six Underground'},
{'genre' => 'Fantasy', 'title' => 'Practical Magic'}
]
);
#adding 'green peppers' to the list of toppings
my $rf = $hash{pizza_toppings};
$rf -> [5] = 'green peppers';
print "My ideal pizza has ";
#Printing out the list of toppings
foreach my $i ( @{ $hash{pizza_toppings} } ) {
print $i . ", ";
};
print ".\nI like to watch ";
#printing out the list of genres
foreach my $i ( @{ $hash{movies} } ) {
my %g = (%{$i});
print $g {'genre'} . ", ";
};
say " movies.";
print "Some of my favourites are ";
#printing out the list of titles
foreach my $j ( @{ $hash{movies} } ) {
my %t = (%{$j});
print $t {'title'} . ", ";
};
say "!";
Upvotes: 2
Views: 95
Reputation: 385764
You want to format multiple lists of items, so let's create a subroutine.
sub format_list {
return "nothing" if @_ == 0;
return $_[0] if @_ == 1;
# ...
}
say "My ideal pizza has ", format_list( @{ $hash{pizza_toppings} } ), ".";
The above handles the two edge cases: When the list is empty, and when it only has one item.
So what's the missing part? Well, you want
XXX, XXX, ... XXX, and XXX
There are two ways of looking at this.
Approach A
XXX, XXX, ... XXX, and XXX
\_______________/\____/\_/
\ \ \__ Last item
\ \____ Glue
\_________ A list of items separated by ", "
join
is an obvious choice here.
return join(", ", @_[ 0 .. $#_ - 1 ]) . ", and " . $_[-1];
@_
is the array of arguments,
@_[...]
returns the identified elements from @_
,
$#_
is the last index of array @_
, and
$_[-1]
is the last argument.
Approach B
XXX, XXX, ... XXX, and XXX
\_________________/\__/\_/
\ \ \__ Last item
\ \____ Glue
\_______ A list of items all ending with ", "
my $s = "";
for my $i ( 0 .. $#_ - 1 ) {
$s .= $_ . ", ";
}
return $s . "and " . $_[-1];
or
return join("", map { "$_, " } @_[ 0 .. $#_ - 1 ]) . "and " . $_[-1];
Tips:
$rf -> [5] = 'green peppers';
is better written as push @{ $rf }, 'green peppers';
or push $rf->@*, 'green peppers';
The space in $g {'genre'}
is really weird.
In a hash index expression, you can drop the quotes of string literals when the string is an identifier (a valid var name, like genre
).
my %g = (%{$i}); ... $g{'genre'}
needlessly creates a copy of the entire hash. $i->{genre}
would be better.
Upvotes: 3