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

This screws me up everytime. I have a delimited list in a format defined externally that I'd like to read into a list of anonymous hashes, so I can do things with it later. So, what I do at the moment is get the fieldnames from the first line with (there can be an arbitrary order and number of fields):
@fieldnames = split(/$separator/,$firstline);
then for each line after,
@fields = split(/$separator/,$line); @data{@fieldnames} = @fields; # [1]
to get a hash of the data as field=value pairs. How do I get a list of these saved up? it isn't push(@datalist,\%data); because that's just a ref to the one hash. I think I need a line like the 1 but to produce a hashref to an anonymous hash instead, but what is it?

Replies are listed 'Best First'.
Re: list of hashrefs?
by chromatic (Archbishop) on Jan 31, 2001 at 21:06 UTC
    I'd have to see more of your code to be sure, but if you're using a global %data, you'll definitely run into clobbering problems, because you're assigning a reference to the *same* hash each time. You can take two approaches:
    while (<DATA>) { chomp; my @fields = split(/$sep/, $_); my %data; @data{@fieldnames} = @fields; push @datalist, %data; }
    Or:
    while (<DATA>) { chomp; my $data = {}; @$data{@fieldnames} = split(/$sep/, $_); push @datalist, $data; }
    I'd choose the first, as it's sometimes tricky to get at lists from hash references, and it's more clear. I would get rid of @fields, though, as it's a synthetic variable.

    The trick here is making sure you have a new lexical %data each time. Just a guess, but it's bitten me a few times.

    Update: Yes, the push line in the first example should use \%data. That'll teach me to get up early.

      Thanks! I'd realised I was reusing the same hash with a little Data::Dumper experimenting, but I couldn't figure out how to 'detach' the current hash - the lexical %data does that.

      That all said, I just tried both of your suggestions, and the first leaves me with a single list of junk, while the second does the deal. %data in a list context like push just dumps out the contents of the hash as field-value pairs, doesn't it? So wouldn't that be \%data ? (just tested, and that works)

Re: list of hashrefs?
by jeroenes (Priest) on Jan 31, 2001 at 21:59 UTC
    Another approach would be to read the whole dataset in one fell swoop:
    $array = supersplit( $separator, \*DATA); #// not needed here
    and than use the first line for your fields as in: @fields = @{ shift( @$array ) }; of course store the indici %indici{@fields} = 0..$#fields; an item is retrieved like: $item = $array[$row][$indici{$thefield}];or even with
    $item = fetchit( $row, $thefield ); sub fetchit{ $r = shift; $f = shift; $array[$r][$indici{$f}]; }
    Hope this helps,

    Jeroen

    Oh yeah, supersplit ca be found here.
    "We are not alone"(FZ)

Re: list of hashrefs?
by petral (Curate) on Feb 01, 2001 at 03:16 UTC
    Just to work the basics out for myself, I did this:
    #!/usr/local/bin/perl -lw ;# 16:48 31Jan01 + colrow.pl $_="a b c\n1 10 100\n2 20 200\n"; @lns=split /\n/; @cols=split / /, shift @lns; # hash of column => colno, array of rows, each row an array indexed by + colno # "row major order" ? $cdex{$cols[$_]} = $_ for 0..$#cols; for (@lns) { push @rows, [(split)] } print " @cols"; print "$_: @{$rows[$_]}" for 0 .. $#rows; print map{" $_"} 0..$#rows; for $c (@cols) { print "$c:", map { " $rows[$_][$cdex{$c}]" } 0..$#rows } # a b c # 0: 1 10 100 # 1: 2 20 200 # 0 1 # a: 1 2 # b: 10 20 # c: 100 200 # hash of column => array of rowvals for that column # "column major order" ? for (@lns) { @row = split; for (@cols) { push @{$data{$_}}, shift @row } $nrow++ } $nrow--; #:( print map{" $_"} 0..$nrow; print "$_: @{$data{$_}}" for @cols; print " @cols"; for $ix (0 .. $nrow) { print "$ix:", map { " $data{$_}[$ix]" } keys %data } # 0 1 # a: 1 2 # b: 10 20 # c: 100 200 # a b c # 0: 1 10 100 # 1: 2 20 200


    p