Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Hashes hunt me even when I sleep....

by bman (Sexton)
on Oct 05, 2000 at 23:36 UTC ( [id://35462]=perlquestion: print w/replies, xml ) Need Help??

bman has asked for the wisdom of the Perl Monks concerning the following question:

O.K. I'm going out on a limb here. I am VERY fresh to PERL so I still try to get all the concepts right in my head. I took upon myself a task of creating a "simple" PERL script that would generate for me a list of artists, their albums and songs.

So far, I have come up with this script:

while ($line = <ALBUMS>) {
	
	($myArtist, $myAlbum, $mySong, $myLocation) = split('\|', $line);
	$mySongs{$myLocation} .= $mySong . "+";
}

foreach $myLocation (sort keys %mySongs) {
	++$i;
	@mySongs = split('\+', $mySongs{$myLocation});
	
	foreach $mySong (@mySongs) {
		print "$mySong\n";
	}
}
That onlyl gives me a list of songs and in this case, I would not have to even use hash (let's call it a mental excersise). What I want to do is this:

Artist:
     Album:
         Songs:
            1.
            2.
     Album2:
         Songs:
            1.
            2.
Artis2:

and so on. In essence I want a grouped report. How can I "attact" this issue (a sample code would be greatly appreciated)?

Thanks.

Replies are listed 'Best First'.
Re: Hashes hunt me even when I sleep....
by le (Friar) on Oct 05, 2000 at 23:55 UTC
    my %songs; while (<DATA>) { my ($myArtist, $myAlbum, $mySong, $myLocation) = split /\|/; $songs{$myArtist}->{$myAlbum}->{$mySong} = $myLocation; } foreach my $myArtist (sort keys %songs) { print "$myArtist:\n"; foreach my $myAlbum (sort keys %{ $songs{$myArtist} }) { print "\t$myAlbum\n"; print "\t\tSongs:\n"; foreach my $mySong (sort keys %{ $songs{$myArtist}->{$myAlbum} + }) { print "\t\t\t$mySong: $songs{$myArtist}->{$myAlbum}->{$myS +ong}\n"; } } }
Re: Hashes hunt me even when I sleep....
by rlk (Pilgrim) on Oct 06, 2000 at 02:14 UTC

    The code others have posted above is good, so I'll simply go into a little more depth about what it does.

    Once you've got $artist, $album, and $song for a given line, you're going to want to put them into a nested hash.

    Assume %songs is your "main" hash. What it's going to contain is references to "anonymous" hashes, one for each artist, keyed by the name of the artist. Each of those hashes will contain references to hashes for each album, keyed by album title. Each album hash will contain, keyed by the name of the song, some piece of information about the song, such as it's filename. If you don't want to keep any information about the song, you can use an array for the innermost element, instead.

    To access an individual element, look it up as

    $songs{"artistname"}->{"albumname"}->{"songname"} #[----] # %songs is a hash #[------------------] # each value in %songs is a reference, to a hash or "hashref". #[--------------------] # the "->" dereferences, so we've got a hash to look up # "albumname" in. #[---------------------------------] # the look up in the inner hash returns another hashref #[-----------------------------------] # so we dereference again, and we're dealing with another # hash... #[----------------------------------------------] # ...in which we look up "songname", getting whatever.
    (The ->'s are optional between '}' and '{', by the way.)

    To print the whole thing out again, simply use nested foreach loops to iterate through each element.

    see perlref for more details, or the second half of Chapter 4 of the Camel Book

    --
    Ryan Koppenhaver, Aspiring Perl Hacker
    "I ask for so little. Just fear me, love me, do as I say and I will be your slave."

    Update:Sorry, I removed the mangled HTML. I had commented something out, and it took out half the page with it. I feel especially dumb, having done this once already on E2

(jeffa) Re: Hashes hunt me even when I sleep....
by jeffa (Bishop) on Oct 05, 2000 at 23:48 UTC
    I always number my song titles, so this solution doesn't exactly address your problem, but it's close enough.

    This will populate a hash:

    # warning: untested my %collection; while(<ALBUMS>) { my ($artist, $album, $song) = split; push @{$collection{$artist}{$album}}, $song; }
    And this will retrieve the data back, unsorted:
    foreach my $artist (keys %collection) { print "$artist:\n"; foreach my $album (keys %{$collection{$artist}}) { print "\t$album:\n\t\t"; print join("\n\t\t", @{$collection{$artist}{$album}}), "\n"; } }
    Hope this helps, Jeff

    UPDATE: oops, yes isotope - that's what I meant.

Re: Hashes hunt me even when I sleep....
by kilinrax (Deacon) on Oct 05, 2000 at 23:52 UTC
    Something like:
    while ($line = <ALBUMS>) { ($artist, $album, $song, $location) = split('\|', $line); $songlist{$artist}{$album} .= $song . "+"; } foreach $artist (sort keys %songlist) { print "Artist: $artist\n"; foreach $album (sort keys %{$songlist{$artist}}) { print " Album: $album\n"; print " Songs:\n"; $i = 0; @songs = split('\+', $songlist{$artist}{album}); foreach $song (@songs) { $i++; print " $i. $song\n"; } } }
    (Though this code is untested)
RE: Hashes hunt me even when I sleep....
by bman (Sexton) on Oct 06, 2000 at 07:26 UTC
    To all who posted here: MANY THANKS! from the top of my lungs. Thanks for the tips, code examples and thorugh explanations. There is a saying that "you can bake a cake in many different ways." Oh boy, am I going to get a ride writing in PERL or what? .... :-)
      Haha..
      I think my favorite node here that demonstrates how many different ways there are to "bake a cake" with PERL is this one.
Re: Hashes hunt me even when I sleep....
by bman (Sexton) on Oct 07, 2000 at 06:31 UTC
    This is my "final" PERL code:

    #!/usr/bin/perl -w
    
    $myFile = "/home/mydata/playlist.txt";
    
    print "Content-Type: text/html\n\n";
    print "<html><head><title>My Song List</title></head>\n";
    print "<body>\n";
    
    
    open(ALBUMS, "$myFile") or die "Can't open $myFile: $!\n";
    while ($line = <ALBUMS>) {
    	($song, $artist, $album, $location) = split('\|', $line);
    	$songlist{$artist}->{$album} .= $song . "\+";
    	$locations{$song} .= $location . "\+";
    }
    
    foreach $artist (sort keys %songlist) {
    	print "<b>Artist:</b> ";
    	
    	if (!$artist) {
    		print "\tNot Provided\n";
    	} else {
    		print "$artist\n";
    	}
    	
    	foreach $album (sort keys %{$songlist{$artist}}) {
    		print "<blockquote>\n";
    		print "\t<b>Album:</b>";
    		
    		if (!$album) {
    			print "\tNot Provided\n";
    		} else {
    			print "\t$album\n";
    		}
    		
    		print "<blockquote>\n";
    		print "\t\t<b>Songs:</b><br>\n";
    		$i = 0;
    		@songs = split('\+', $songlist{$artist}{$album});
    		foreach $song (@songs) {
    			@thislocation = split('\+', $locations{$song});
    			foreach $location (@thislocation) {
    				++$i;
    				print "\t\t\t$i. <a href=\"http://$location\">$song</a><br>\n";
    			}
    		}
    		print "</blockquote></blockquote>\n";
    	}
    }
    
    Now, mind you that this is just a beginning, something to start on. I'm not sure if anybody else would do this the same way (probably not) but in this case it does a job. I'll throw later some search capabilities and off to fun we go!

    P.S. What scares me the most is the fact that I haven't fully realized how I accomplished this task. I was playing with this code until I got it right. It's going to take some time before I feel comfortable with hashes, dereferencing and other "goodies." Meantime, I will enjoy what I have accomplished with you help! :-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://35462]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2024-04-19 10:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found