Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Sorting hashes with new value detection

by sulfericacid (Deacon)
on Jan 24, 2006 at 17:39 UTC ( [id://525255]=perlquestion: print w/replies, xml ) Need Help??

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

I have a hash that has numeric values. I need to be able to sort out these values and print a header for each NEW value.

my %values = ( "apple" => "1", "pear" => "3", "orange" => "3", "melon" => "2", "grape" => "2");
To yield:
Level 3: Pear, Orange Level 2: Melon, Grape Level 1: apple
Kutsu tried to help with the code on his pad. The idea was to use HOA or AOAs and I haven't any experience with those (or references) so I'm kind of lost. What I have to play with is
my %hash = ( 1 => ["apple"], 2 => ["melon", "grape"], 3 => ["pear", "orange"] ); $hash{1}[1] = "pickle"; print "Level $_\n@{$hash{$_}}\n\n" for reverse sort keys %hash;
Which works. The problem I am having is trying to figure out how this would work with dynamic data. I'll never know what "levels" exist or how many there are, so using indexes probably won't work properly.

In the end, I need to be able to sort and rip parts and pieces of this with ease (like a typical hash). Things to keep in mind are; I need to be able to sort by length of characters in the words, and find specific elements which words start with (or end with) specific characters. Ideally, it'd need to be as maintainable as a hash which I'm sure HOAs and AOAs are capable of.

I'm just new to the multidimensional world and could use some insight.



"Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

sulfericacid

Replies are listed 'Best First'.
Re: Sorting hashes with new value detection
by Roy Johnson (Monsignor) on Jan 24, 2006 at 17:43 UTC
    You can turn your structure into the new structure like so:
    my %hash; while (my ($k,$v) = each %values) { push @{$hash{$v}}, $k; }
    So just keep it as a flat hash until you need the other structure (at the end, to print), then convert it.

    Caution: Contents may have been coded under pressure.
Re: Sorting hashes with new value detection
by kutsu (Priest) on Jan 24, 2006 at 18:00 UTC

    Code from my pad (in order to be more permenant):

    my %hash = ( 1 => ["apple"], 2 => ["melon", "grape"], 3 => ["pear", "orange"] ); print "Level $_\n@{$hash{$_}}\n\n" for keys %hash; #or possibly my @array = (["apple"], ["melon", "grape"], ["pear", "orange"]); print "Level ", ++$_, "\n@{$array[$_]}\n\n" for 0 .. $#array;

    Note, it merely prints the "level structure", twice :), sulfericacid asked for in his orginal question (on the CB).

Re: Sorting hashes with new value detection
by serf (Chaplain) on Jan 24, 2006 at 18:25 UTC
    If you wanted to do it using map you could do this:
    my %hash; map { push( @{ $hash{ $values{$_} } }, $_ ) } keys %values;
    which if you were golfing would save you a few characters on Roy Johnson's probably easier to read version:
    while(my($k,$v)=each%values){push@{$hash{$v}},$k;}
    which becomes:
    map{push(@{$hash{$values{$_}}},$_)}keys%values;
    and then access the result with:
    for my $key (reverse sort keys %hash) { print "Level $key:\n", join(", ", @{$hash{$key}}), "\n\n"; }
Re: Sorting hashes with new value detection
by Aristotle (Chancellor) on Jan 24, 2006 at 17:52 UTC

    In the end, I need to be able to sort and rip parts and pieces of this with ease (like a typical hash). Things to keep in mind are; I need to be able to sort by length of characters in the words, and find specific elements which words start with (or end with) specific characters. Ideally, it’d need to be as maintainable as a hash which I’m sure HOAs and AOAs are capable of.

    Can you elaborate on your needs? What does the code do?

    Makeshifts last the longest.

      In short, I am creating a little word game. Each character is worth X points, so I record the word in the hash along with the value of the entire word. That's what the numbers are in the example (not accurate, mind you, it's just basic sample code).

      When I print out scores, I'll need to be able to sort by the length of the word and/or sort by the word values. When sorted by value, I need to be able to display all words in specific categories by their value. All 5 pt words are grouped together, as so on.

      Thank you.



      "Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

      sulfericacid

        Ah. Do you ever need to look up the value of word based on the word? (Ie. do you need the mapping that your simple hash provides?) If not, I’d simply keep this in the HoA as outlined by Roy Johnson and stick values inside using push @{ $words{ $score } }, $word;

        Otherwise, I’d either keep both structures updated throughout the code (which means putting the push and hash assignment in a sub), or generate one of them out of the other on the fly and demand.

        Makeshifts last the longest.

        Something like this perhaps?

        use warnings; use strict; my %lookup = ( a => 2, b => 4, c => 4, d => 4, e => 1, f => 5, g => 6, h => 3, i => 2, j => 7, k => 7, l => 4, m => 3, n => 3, o => 2, p => 5, q => 11, r => 5, s => 3, t => 3, u => 2, v => 4, w => 7, x => 10, y => 7, z => 9 ); my @scores; while ( my $line = <DATA> ) { chomp $line; my @words = split /\s+/, $line; for my $word (@words) { $word =~ s/[^A-Za-z]//g; next unless length $word; my @letters = split //, $word; my $score; $score += $lookup{ lc $_ } for @letters; push @{ $scores[$score] }, $word; } } for my $score ( 0 .. $#scores ) { if ( defined @{ $scores[$score] } ) { my %words; @words{ @{ $scores[$score] } } = undef; print 'Scored ', $score, ":\n"; print " $_\n" for sort { length $a <=> length $b || $a cmp $b } keys %words; print "\n"; } } __DATA__ In short, I am creating a little word game Each character is worth X points, so I record the word in the hash along with the value of the entire word. That's what the numbers are in the example (not accurate, mind you, it's just basic sample code). When I print out scores, I'll need to be able to sort by the length of the word and/or sort by the word values. When sorted by value, I need to be able to display all words in specific categories by their value. All 5 pt words are grouped together, as so on.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (6)
As of 2024-04-20 00:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found