Reputation: 327
I know this topic has been covered but other posts usually has static hashes and arrays and the don't show how to load the hashes and arrays. I am trying to process a music library. I have a hash with album name and an array of hashes that contain track no, song title and artist. This is loaded from an XML file generated by iTunes.
the pared down code follows:
use strict;
use warnings;
use utf8;
use feature 'unicode_strings';
use feature qw( say );
use XML::LibXML qw( );
use URI::Escape;
my $source = "Library.xml";
binmode STDOUT, ":utf8";
# load the xml doc
my $doc = XML::LibXML->load_xml( location => $source )
or warn $! ? "Error loading XML file: $source $!"
: "Exit status $?";
my %hCompilations;
my %track;
# extract xml fields
my @album_nodes = $doc->findnodes('/plist/dict/dict/dict');
for my $album_idx (0..$#album_nodes) {
my $album_node = $album_nodes[$album_idx];
my $trackName = $album_node->findvalue('key[text()="Name"]/following-sibling::*[position()=1]');
my $artist = $album_node->findvalue('key[text()="Artist"]/following-sibling::*[position()=1]');
my $album = $album_node->findvalue('key[text()="Album"]/following-sibling::*[position()=1]');
my $compilation = $album_node->exists('key[text()="Compilation"]');
# I only want compilations
if( ! $compilation ) { next; }
%track = (
trackName => $trackName,
trackArtist => $artist,
);
push @{$hCompilations{$album}} , %track;
}
#loop through each album access the album name field and get what should be the array of tracks
foreach my $albumName ( sort keys %hCompilations ) {
print "$albumName\n";
my @trackRecs = @{$hCompilations{$albumName}};
# how do I loop through the trackrecs?
}
Upvotes: 0
Views: 80
Reputation: 69224
This line isn't doing what you think it is:
push @{$hCompilations{$album}} , %track;
This will unwrap your hash into a list of key/value pairs and will push each of those individually onto your array. What you want is to push a reference to your hash onto the array.
You could do that by creating a new copy of the hash:
push @{$hCompilations{$album}} , { %track };
But that takes an unnecessary copy of the hash - which will have an effect on your program's performance. A better idea is to move the declaration of that variable (my %track
) inside the loop (so you get a new variable each time round the loop) and then just push a reference to the hash onto your array.
push @{$hCompilations{$album}} , \%track;
You already have the code to get the array of tracks, so iterating across that array is simple.
my @trackRecs = @{$hCompilations{$albumName}};
foreach my $track (@trackRecs) {
print "$track->{trackName}/$track->{trackArtist}\n";
}
Note that you don't need the intermediate array:
foreach my $track (@{$hCompilations{$albumName}}) {
print "$track->{trackName}/$track->{trackArtist}\n";
}
Upvotes: 2
Reputation: 2331
first of all you want to push the hash as a single element, so instead of
push @{$hCompilations{$album}} , %track;
use
push @{$hCompilations{$album}} , {%track};
in the loop you can access the tracks with:
foreach my $albumName ( sort keys %hCompilations ) {
print "$albumName\n";
my @trackRecs = @{$hCompilations{$albumName}};
# how do I loop through the trackrecs?
foreach my $track (@trackRecs) {
print $track->{trackName} . "/" . $track->{trackArtist} . "\n";
}
}
Upvotes: 2