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

I have many 3 item groupings (i.e. US,USA,USD) and I'd like to maintain a data structure in which given one I can easily write small functions to convert between the other two. I have been playing around with a few data structures, mainly:
my %countryMap = ( US => [USA,USD],...);
my $countryMap = { US => {country=>USA, currrency=>USD}, ... };
with these data structures, I can't seem to figure out a way to convert from USA to US and USD to US. can anyone help? thanks.

Replies are listed 'Best First'.
Re: data structure problem
by Zaxo (Archbishop) on Jun 06, 2002 at 21:45 UTC

    From your statement of the problem, it sounds like all the codes are unique. That allows you to use a translation hash. Lets set one up starting with an Array-of-Arrays that is built directly from your data.

    my @code_data = [ ['US','USA','USD'], # ... ]; my %translator = map { $_->[0] => $_, $_->[1] => $_, $_->[2] => $_ } @code_data; use constant ccode => 0; use conatant country => 1; use constant currency => 2; print $translator{USA}[currency]; print $translator{USD)[ccode];
    You have a problem with the design which will make this construction fail as it is. What country is supposed to come back when you say $translator{EURO}[country]? Your question does not hold up on meeting real data.

    The currency elements need to hash to an array. You may have other data with similar properties. It doesn't have to be painful, but it's not as simple as above:

    # construct %translator as before, but only for unique keys for (@code_data) { push @{$translator{$_->[currency]}}, $_; }
    That makes your lookups a little more complicated, but that is dictated by the problem domain.

    After Compline,

Re: data structure problem
by Anonymous Monk on Jun 06, 2002 at 20:59 UTC
    How about a two level hash representing conversions? A little redundancy, but convenient access.
    my %convert = ( to_currency => { US => 'USD', USA => 'USD', MR => 'MRD', MARS => 'MRD', }, to_country => { US => 'USA', USD => 'USA', MR => 'MARS', MRD => 'MARS', }, to_code => { USA => 'US', USD => 'US', MARS => 'MR', MRD => 'MR', }, ); my $code = 'US'; print "$code currency is: $convert{to_currency}{$code}\n"; print "$code country is: $convert{to_country}{$code}\n"; my $currency = 'USD'; print "Code for $currency is: $convert{to_code}{$currency}\n"; my $country = 'MARS'; print "$country currency is: $convert{to_currency}{$country}\n";
Re: data structure problem
by jwest (Friar) on Jun 06, 2002 at 21:03 UTC
    Presupposing that 'country' and 'currency' will always be unique keys (and renaming your old %countryMap to %codeMap because the label made more sense to me in this case), this would work...
    my %codeMap = ( US => { country => 'USA', currency => 'USD'} ); my %currencyMap = map { $codeMap{$_}{currency} => $_ } keys(%codeMap); my %countryMap = map { $codeMap{$_}{country} => $_ } keys(%codeMap);

    But, you probably want to use a database for this.
    Hope this helps!


    -><- -><- -><- -><- -><-
    All things are Perfect
        To every last Flaw
        And bound in accord
             With Eris's Law
     - HBT; The Book of Advice, 1:7
Re: data structure problem
by George_Sherston (Vicar) on Jun 06, 2002 at 22:07 UTC
    Another Way To Do It - an array_ref of hashes:
    $countryMap = [ { shortcountry => 'US', country => 'USA', currency => 'USD', }, { shortcountry => 'D', country => 'Germany', currency => 'DM', }, .... ];
    This is basically a flatfile db in memory - and can be easily saved to a file using Data::Dumper. The problem is getting the data out. I don't see a single step way to do this, but hey, we're optimising for developer time here, not cpu / memory. I'd do
    sub getem { my $data = shift; my $find = shift; my $in = shift; my $return = shift; for (@$data) { if ($_->{$in} eq $find) { return $_->{$return}; } } }
    Then to find 'US' with 'USA',
    my $result = getem($countryMap,'US','country','shortcountry');

    George Sherston
Re: data structure problem
by Stegalex (Chaplain) on Jun 06, 2002 at 23:22 UTC
    Just writing to make you aware of a module called Locale::Country which is available on CPAN. This module keeps track of the official currency and country abbreviations outlined in ISO-3166. It's worth a look

    I like chicken.
Re: data structure problem
by Abigail-II (Bishop) on Jun 07, 2002 at 11:58 UTC

    If you were to store this in a relational database, you could just dump it in a table and build three indices on top of it. Well, we can do something similar in Perl (except that our indices are not at all suitable for range queries), using hashes for the indices.

    Here's some example code (assuming the codes and countries are unique, but currencies aren't).

    #!/usr/bin/perl -w use strict; use warnings 'all'; my (%code, %country, %currency); while (<DATA>) { my $info = [split]; $code {$info -> [0]} = $info; $country {$info -> [1]} = $info; push @{$currency {$info -> [2]}} => $info; } sub info_by_code { my $code = shift; $code {$code} } sub info_by_country { my $country = shift; $country {$country} } sub info_by_currency { my $currency = shift; @{$currency {$currency}} } print "Code for Germany is ", info_by_country ("GER") -> [0], "\n"; print "People pay in Euros in ", join (", " => map {$_ -> [1]} info_by_currency ("EUR")), "\n"; __END__ US USA USD CA CAN CND DE GER EUR IT ITA EUR FR FRA EUR NL NET EUR BE BEL EUR CH SWI SFR $ ./countries Code for Germany is DE People pay in Euros in GER, ITA, FRA, NET, BEL $


Re: data structure problem
by robobunny (Friar) on Jun 06, 2002 at 21:01 UTC
    you could build three hashes :) otherwise, i don't think you are going to get away from painful searching. or you could use a relational database, but that seems like a bit much...