Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Converting a hash element into an array

by rockers (Initiate)
on Jan 19, 2009 at 11:17 UTC ( [id://737262]=perlquestion: print w/replies, xml ) Need Help??

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

Hi, This is my code which tries to get a hash (with exactly 4 inputs) from the user.
use strict; use warnings; sub getHash { my %hash; for(my $i=0;$i<4;$i++) { my $key = <STDIN>; chomp($key); my $val = <STDIN>; chomp($val); $hash{$key} = $val; } }
However, what I want is a bit more intelligence put to this function. For eg, if the user wants to put 2 elements into the same key, then the 2nd element must be 'push'ed to the already existing hash element, instead, this function, naively replaces the first element with the second, in case of such repetition. Can somebody help me in this regard? Thanks in advance.

Replies are listed 'Best First'.
Re: Converting a hash element into an array
by almut (Canon) on Jan 19, 2009 at 11:23 UTC

    In case you want to start using an array for the entry as soon as there is more than one element (and only then), you could do

    ... my $key = <STDIN>; chomp($key); my $val = <STDIN>; chomp($val); if (exists $hash{$key}) { $hash{$key} = [ $hash{$key} ] unless ref($hash{$key}); push @{$hash{$key}}, $val; } else { $hash{$key} = $val; }

    Update: this would produce a data structure like this

    $VAR1 = { 'bar' => 'val3', 'baz' => 'val4', 'foo' => [ 'val1', 'val2' ] };

    Whether that's convenient to use further on, is another question... Maybe you'd want to always use an array for each entry, in which case you could simply write (using autovivification):

    ... my $key = <STDIN>; chomp($key); my $val = <STDIN>; chomp($val); push @{$hash{$key}}, $val;

    producing:

    $VAR1 = { 'bar' => [ 'val3' ], 'baz' => [ 'val4' ], 'foo' => [ 'val1', 'val2' ] };
Re: Converting a hash element into an array
by holli (Abbot) on Jan 19, 2009 at 11:31 UTC
    use strict; use warnings; sub getHash { my %hash; for(my $i=0;$i<4;$i++) { my $key = <STDIN>; chomp($key); my $val = <STDIN>; chomp($val); if ( !defined $hash{$key} ) #first time { $hash{$key} = $val; } else { $hash{key} = ref($hash{key}) ? [ @{$hash{key}}, $val ] : [ $ha +sh{key}, $val ]; } } }
    This leaves you however with the ambuigity of deciding wether there is an arrayref or a scalar under the keys of your hash in any code that processes the hash later on. It might be simpler to always use an arrayref, even when there's only a sngle element.
    use strict; use warnings; sub getHash { my %hash; for(my $i=0;$i<4;$i++) { my $key = <STDIN>; chomp($key); my $val = <STDIN>; chomp($val); push @{$hash{key}}, $val; } }


    holli, /regexed monk/
      However, I need to maintain the distinction of scalar and array for different inputs. So, the first code really suites me! :)
      Thanks again!
        You can also distinguish that by looking at the number of elements of the array.


        holli, /regexed monk/
Re: Converting a hash element into an array
by luckypower (Beadle) on Jan 19, 2009 at 11:41 UTC
    Hi, You can assing one empty array to each key element and push the values in that array.

    like this

    my $key = <STDIN>; chomp($key); $hash{$key} = [] unless $hash{$key}; my $val = <STDIN>; chomp($val); push @{$hash{$key}}, $val;
      @luckypower and @holli:
      Thanks, I was wrong. Declaring it as an array apriori is better.
Re: Converting a hash element into an array
by hbm (Hermit) on Jan 19, 2009 at 13:35 UTC
    And you can condense that a bit:
    for (0..3) { chomp(my $key = <STDIN>); chomp(my $val = <STDIN>); push @{$hash{$key}}, $val; }
Re: Converting a hash element into an array
by jethro (Monsignor) on Jan 19, 2009 at 18:26 UTC

    Entering 4 pairs this way is uncomfortable and error prone (forget to enter one item or press return twice and you have keys and values switched). If (for example) '=' can't occur in any key, entering them as 'key = value' might be better:

    my $all = <STDIN>; chomp($all); if ($all=~/([^=]+?)\s*=\s*(.*)/) { $hash{$1} = $2; } else { print "Input Error. \n"; $i--; }
Re: Converting a hash element into an array
by dorward (Curate) on Jan 19, 2009 at 11:19 UTC
    I wrote the following in JavaScript a few years ago. The logic should be the same in Perl, but I'll leave porting it as an exercise for the reader.
    var query_string = {}; var query = window.location.search.substring(1); var vars = query.split("&"); for (var i=0;i<vars.length;i++) { var pair = vars[i].split("="); // If first entry with this name if (typeof query_string[pair[0]] === "undefined") { query_string[pair[0]] = pair[1]; // If second entry with this name } else if (typeof query_string[pair[0]] === "string") { var arr = [ query_string[pair[0]], pair[1] ]; query_string[pair[0]] = arr; // If third or later entry with this name } else { query_string[pair[0]].push(pair[1]); } }

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (5)
As of 2024-04-25 14:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found