Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Multiple keys for a hash table row?

by mike65535 (Novice)
on Dec 10, 2015 at 19:32 UTC ( [id://1149929]=perlquestion: print w/replies, xml ) Need Help??

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

I have a hash table like this:
my %foo_commands = { "GENERATE" => "10 14 00 07 4d", "DELIVER" => "16 75 32 14 77" ... };
Can I make something like this:
my %foo_commands = { "GENERATE", "CREATE" => "10 14 00 07 4d", "DELIVER", "DELIV" => "16 75 32 14 77" ... };
I know that literally I cannot (I tried it!), but is there a workable construct? It should be clear I do not want to duplicate the rows, making one per spelling (that's the whole point).

In the end I'd like to have several alternative "keys" per row, including some subset of abbreviations.

(Note: The code that "looks up" the row in the hash table uses a command line argument so I want to give the user the most flexibility)

Replies are listed 'Best First'.
Re: Multiple keys for a hash table row?
by SuicideJunkie (Vicar) on Dec 10, 2015 at 19:53 UTC

    If your values are simple, you can just make copies such as by saying:

    my %commands; $commands{$_} = "10 14 00 07 4d" for qw(GENERATE CREATE SPAWN ETC); $commands{$_} = "16 75 32 14 77" for qw(DELIVER DELIV SHIP ETC2);

    If your values are more complex (IE; references to larger things), then simply make both references point to the same object.

    my %commands; my $createCMD = {weight=>10, coordinates=>[42,-3.14], logic=>sub{ ... +}, sound=>'success.mp3'}; $commands{$_} = $createCMD for qw(GENERATE CREATE SPAWN);

Re: Multiple keys for a hash table row?
by jeffa (Bishop) on Dec 10, 2015 at 19:49 UTC

    Just make new hash keys that reference the original keys' values:

    use strict; use warnings; my %foo = ( "GENERATE" => "10 14 00 07 4d", "DELIVER" => "16 75 32 14 77" ); $foo{CREATE} = \$foo{GENERATE}; $foo{DELIV} = \$foo{DELIVER}; use Data::Dumper; print Dumper \%foo;

    However, now you need to de-reference the values to use them:

    print ${ $foo{CREATE} }, $/;

    I recommend just duplicating the data:

    $foo{CREATE} = $foo{GENERATE}; $foo{DELIV} = $foo{DELIVER};
    Unless you know you will be dealing with million and millions of hash keys. Clarity is very important. Also, check the CPAN for modules that allow you to alias hash keys.

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      I'll have very few hash keys (a dozen or so).

      Likely I'll have way more aliases.

      Thanks.

      Mike

Re: Multiple keys for a hash table row?
by Discipulus (Canon) on Dec 10, 2015 at 21:44 UTC
    Hello mike6535,
    In the end I'd like to have several alternative "keys" per row, including some subset of abbreviations.
    The behaviour you described is exactly what GetOpt::Long does with arguments. Take a loong breath and dive in it's source code to see how a real world wheel works.

    Yes! you can reinvent the wheel (but see my signature..). In this case you can profit of the core module Text::Abbrev to have all unique abbreviations from a list, but you need to check for duplicates and collisions...

    Probably have a dedicated sub to get the job done. Let's assume we want that the keys are in different languages and that values are just strings of words like in your example; something like (semitested!):
    use strict; use warnings; my %multikeys; my %yet_used; # alternatives # value multikeyize ('colors|colores|farben|col|c',"red green purple"); multikeyize ('animals|animalia|a',"sheep cow dog"); # warning example: redefining a masterkey # multikeyize ('colors|xss|x',"tizio caio sempronio"); # warning example: redefining an alias # multikeyize ('c|xss|x',"tizio caio sempronio"); sub multikeyize{ my $alternatives= shift; my $value = shift; my @alternate = split /\|/,$alternatives; my $masterkey = shift @alternate; # masterkeys take the precedence.. if ($yet_used{$masterkey}){ print "\tWARNING '$masterkey' already defined! wiping previous + value!", ( $yet_used{$masterkey}=~/^1$/ ? "(was an alias)" : "(was a masterkey with value of '$yet_used{$masterkey} +')" ),"\n"; undef $yet_used{$masterkey}; } $multikeys{$masterkey} = $yet_used{$masterkey} = $value; map { $yet_used{$_}++; $multikeys{$_} = \$multikeys{$masterkey}; # just to show it.. print "$_->"; } grep {not exists $yet_used{$_} } @alternate; # just to show it.. print "(masterkey) $masterkey = '$multikeys{$masterkey}'\n\n"; } __OUTPUT__ colores->farben->col->c->(masterkey) colors = 'red green purple' animalia->a->(masterkey) animals = 'sheep cow dog'


    HtH
    L*
    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Multiple keys for a hash table row?
by GotToBTru (Prior) on Dec 10, 2015 at 21:33 UTC

    You want multiple values to point to the same hash key. You could use a hash to convert multiple values into one.

    %convert = (Create=>'Create', Generate=>'Create', Deliver=>'Deliver', Deliv=>'Deliver'); %foo_commands = ( Create => "10 14 00 07 4d", Deliver => "16 75 32 14 77"); print $foo_commands{$convert{Deliv}};
    Dum Spiro Spero
Re: Multiple keys for a hash table row?
by AnomalousMonk (Archbishop) on Dec 11, 2015 at 04:21 UTC

    This is just a variation on SuicideJunkie's approach above (update: but with some automatic abbreviation handling added):

    c:\@Work\Perl>perl -wMstrict -le "my %commands; ;; for my $ar_keys_val ( [ qw(GENERATE CREATE) => '10 14 00 07 4d', ], [ qw(DELETE) => '12 34 56 78 90', ], [ qw(DELIVER) => '16 75 32 14 77', ], [ qw(FOO BAR BOFF) => '99 88 77 66 55', ], ) { my @multi_keys = @{ $ar_keys_val }[ 0 .. $#$ar_keys_val - 1 ]; my $single_val = @{ $ar_keys_val }[-1]; for my $mk (@multi_keys) { die qq{duplicate key '$mk' -> '$single_val'} if exists $commands{ +$mk}; $commands{$mk} = $single_val; } }; ;; use Data::Dump qw(dd); ;; dd \%commands; ;; CMD: for my $cmd (qw(FOO BAR XBAR BARR DEL DELI)) { printf qq{command '$cmd': }; my @candidates = grep { 0 == index $_, $cmd } keys %commands; if (not @candidates) { print qq{not found}; next CMD; } if (@candidates > 1) { print qq{ambiguous: @candidates}; next CMD; } print qq{($candidates[0]) -> '$commands{$candidates[0]}'}; } " { BAR => "99 88 77 66 55", BOFF => "99 88 77 66 55", CREATE => "10 14 00 07 4d", DELETE => "12 34 56 78 90", DELIVER => "16 75 32 14 77", FOO => "99 88 77 66 55", GENERATE => "10 14 00 07 4d", } command 'FOO': (FOO) -> '99 88 77 66 55' command 'BAR': (BAR) -> '99 88 77 66 55' command 'XBAR': not found command 'BARR': not found command 'DEL': ambiguous: DELIVER DELETE command 'DELI': (DELIVER) -> '16 75 32 14 77'


    Give a man a fish:  <%-{-{-{-<

Re: Multiple keys for a hash table row?
by GrandFather (Saint) on Dec 10, 2015 at 22:12 UTC

    Use an aliases table that points to the unique key in the data table.

    Oh, I see I wrote "table". I meant hash of course (or did I?).

    Premature optimization is the root of all job security

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2024-04-26 01:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found