Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Mapping to an associative hash

by THRAK (Monk)
on Sep 16, 2003 at 20:57 UTC ( [id://291963]=perlquestion: print w/replies, xml ) Need Help??

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

I have a tab delimited file that is a DB export of an addressbook. I want to map the data from this file into a hash that has associative keys. I can read the data into an array with no problem, but is there an easy way to map this into the hash? Or is there a way to split things into the hash instead of an array? I believe going from the array to a hash isn't too difficult, but I need to get to an associative hash. Maybe array -> hash -> associative hash? Don't know, that's why I'm posting this!

/usr/bin/perl -w use strict; my $import_file = shift || 'addresses'; my @tmp; my %import_data =( 'id' => '', 'username' => '', 'domain' => '', 'address' => '', 'firstname' => '', 'lastname' => '', 'nickname' => '', 'type' => '', 'street' => '', 'city' => '', 'state' => '', 'zip' => '', 'country' => '', 'company' => '', 'home_phone' => '', 'work_phone' => '', 'pager' => '', 'cellular' => '', 'fax' => '', 'other' => '', 'birth_month' => '', 'birth_day' => '', 'birth_year' => '', 'birthday' => 'undef', 'title' => '', 'status' => '', 'time_stamp' => '', 'middleinitial' => '', 'nameprefix' => 'undef', 'namesuffix' => 'undef', 'street2' => '', 'website' => '', 'notes' => '', 'department' => 'undef', 'assistant' => 'undef', 'dstreet' => '', 'dstreet2' => '', 'dcity' => '', 'dstate' => '', 'dzip' => '', 'dcountry' => '', 'dphone1' => 'undef', 'dphone2' => 'undef', 'daddress' => '', 'dspouse' => 'undef', 'danniversary_day' => '', 'danniversary_month' => '', 'dnotes' => 'undef', 'dwebsite' => '', 'daddress2' => '', 'danniversary_year' => '', 'category' => '', 'prime_phone' => '', ); open(FILE, "< $import_file") or die "Cannot open $import_file ERR: $!\ +n"; while (<FILE>) { @tmp = split /\t/; #map @tmp to %import_data here #then call a sub-routine to manipulate } close FILE;

Thank You,

-THRAK
www.polarlava.com

janitored by ybiC: <readmore> tags around long codeblock, as per Monastery convention

Replies are listed 'Best First'.
Re: Mapping to an associative hash
by Thelonius (Priest) on Sep 16, 2003 at 21:06 UTC
    Sure thing! If you have the keys you want in an array @keys (in the order that the data appears in the file), then all you need to say is:
    while (<FILE>) { my %data; chomp; @data{@keys} = split /\t/; # then call subroutine }
Re: Mapping to an associative hash
by Zaxo (Archbishop) on Sep 16, 2003 at 21:21 UTC

    You can use your knowlege of the keys/column names to make the file an array of hashes or, if there can be constructed a 'primary key' from the data, a hash of hashes. Assuming that the 'id' is unique, it might serve. In any case, make a constant array of your column names and fill in a hash slice over that with the data.

    Here is yet another way, making a hash of hashes on id, with the second level hashes constructed directly from a list,

    my @keys = qw( id username domain address); # and so on my %addressbook; while (<>) { # or whatever handle my @data = split /\t/; $addressbook{$data[0]} = { map { ($keys[$_] => $data[$_]) } 1..$#keys }; }
    There are lots of ways to do this.

    After Compline,
    Zaxo

Re: Mapping to an associative hash
by shenme (Priest) on Sep 16, 2003 at 21:20 UTC
    Your records have tab-delimited fields, so "@tmp = split /\t/;" copies those fields, in order, into the array.   Are the fields named in the same order you show in %import_data, that is, is the 'id' value first, then the 'username' value, and so on?   You just need to stuff name/value pairs into your hash using names in the same order as your values.

    my @field_names = qw( id username domain .... prime_phones ); my @field_values = split /\t/; @input_data{@field_names} = @field_values;
    That's using a "hash slice".   A longer parallel version (untested)
    my @field_names = qw( id username domain .... prime_phones ); my @field_values = split /\t/; for( my $i=0; $i<@field_values; ++$i ) { $input_data{$field_names[$i]} = $field_values[$i]; }
Re: Mapping to an associative hash
by IOrdy (Friar) on Sep 17, 2003 at 01:56 UTC
    You could use DBD::CSV and supply the column names when you create the db handle. Then just $sth->fetchrow_hashref like you would any other sql query result.

    update: added some code :)
    my $dbh = DBI->connect(q{DBI:CSV:}); $dbh->{'csv_tables'}->{'mytable'} = { 'file' => 'mytable.csv', 'sep_char' => "\t" 'col_names' => ["col_a", "col_b", "col_c"] }; my $sth = $dbh->prepare("SELECT m.col_a, m.col_c FROM mytable m"); $sth->execute; my @rows; while (my $row = $sth->fetchrow_hashref) { push @rows, $row; }
Re: Mapping to an associative hash
by CountZero (Bishop) on Sep 17, 2003 at 06:02 UTC

    IOrdy's comment made me think of the following: if the data is an export of a database, can't you directly access the database through some of the DBI::DBD combo's?

    CountZero

    "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law

      Your logic is correct, but this is part of a migration project and I don't have access to the database from which the data is coming. This is being supplied by a third-party as tab delimited files.

      -THRAK
      www.polarlava.com
Re: Mapping to an associative hash
by CountZero (Bishop) on Sep 16, 2003 at 21:25 UTC

    Is perhaps a "hash of hashes" what you are looking for? I.e. you have a top-level hash which is keyed by the ID-number and which refers to another hash which has all the keys you mention.

    If that fits your idea, have a look at perldoc perldsc which will explain it in full detail.

    CountZero

    "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law

Re: Mapping to an associative hash
by dragonchild (Archbishop) on Sep 16, 2003 at 21:10 UTC
    hashes are also known as associative ararys. hashing, by its nature, is associative. So, "associative hash" is redundant.

    ------
    We are the carpenters and bricklayers of the Information Age.

    The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (4)
As of 2024-04-19 20:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found