[comp.lang.perl] constructing tables

tchrist@convex.COM (Tom Christiansen) (12/02/90)

In article <EMV.90Nov30194551@poe.aa.ox.com> emv@ox.com (Ed Vielmetti) writes:
>In article <109564@convex.convex.com> tchrist@convex.COM (Tom Christiansen) writes:
>
>   Speaking of frequently asked questions, here are the new ones 
>   for this month (I'll post RSN)....
>   Any other candidates or suggestions?
>
>A nice example of how to load data into an associative array with some
>kind of <<ENDOFTABLE construct would be welcomed (if it's not already
>in there).  Or several associative arrays, for that matter.  I'd like
>to have some way of taking a table
>
>news/readers	rn,nn,trn
>news/nntp	nntp,nntpxmit,nntplink
>gnu		binutils,flex,fileutils
>x11/games	xtrek
>lwall		rn,patch,perl
>ENDOFTABLE
>
>and turning it into
>
>$category{"rn"} -> "news/readers,lwall"
>$category{"nntpxmit"} -> "news/nntp"
>$category{"binutils"} -> "gnu"

I don't think this has been asked very often, so I don't think I'll put it
in the FAQ unless I see it a lot more often.  I don't really envision the
FAQ has a list of how to do neat things in Perl; there'll be plenty of
that in the Book.  It's supposed to be answers to questions about Perl
that people ask again and again.

What you're asked about is, nonetheless, a nice thing to know how to
do, so I'll it answer here.  In general, you can load associative
arrays this way:

    %assoc = ( 'key1', 	'value1', 
	       'key2', 	'value2', 
	       'key3', 	'value3', 
	       ...
	       'keyN', 	'valueN', 
	     );

If you would like this in a <<here document, you can do this:
(I've indented the examples for clarity, but you really need 
the ENDOFTABLE part flush left to the margin, unless you 
say something like <<"\tENDOFTABLE".)

    %assoc = split(' ', <<ENDOFTABLE);
    key1 value1
    key2 value2
    key3 value3
    ...
    keyN valueN
    ENDOFTABLE  

Bear in mind that this will make your program bigger than the
previously described method because you end up storing the data 
as both a string and an array.

Now, in your case, you've got things arranged this way:

    value1	key1a,key1b,key1c
    value2	key2a,key2b
    value3	key3a,key3b,key3c,key3c
    ...

This means you're going to have to do some extra processing 
to turn it around and break it up.

    for (split(/\n/,<<EOTABLE)) {
news/readers	rn,nn,trn
news/nntp	nntp,nntpxmit,nntplink
gnu		binutils,flex,fileutils
x11/games	xtrek
lwall		rn,patch,perl
EOTABLE
	($value, $keys) = split;
	for $key (split(/,/, $keys)) {
	    $category{$key} = $value;
	}
    }

That's a lot of splitting, and probably Randal or Larry will show us
a niftier hack using using less obvious but more Perlian magic.

I can at least cut down on one split and the cost of storing it
all as a string.  Put the table at end of the program past a __END__
symbol and load in in by referencing <DATA>.

    # init
    while (<DATA>) {
	($value, $keys) = split;
	for $key (split(/,/, $keys)) {
	    $category{$key} = $value;
	}
    }

    # rest of program
    # blah blah

    exit 0;

    __END__
    news/readers	rn,nn,trn
    news/nntp		nntp,nntpxmit,nntplink
    gnu			binutils,flex,fileutils
    x11/games		xtrek
    lwall		rn,patch,perl


Does that answer your question?


--tom

emv@ox.com (Ed Vielmetti) (12/03/90)

In article <109679@convex.convex.com> tchrist@convex.COM (Tom Christiansen) writes:

   I can at least cut down on one split and the cost of storing it
   all as a string.  Put the table at end of the program past a __END__
   symbol and load in in by referencing <DATA>.

this is what I end up with once I'm done.  one of the (unstated)
requirements is to have there be only data and not code in the file,
so I'm willing to absorb the subroutine call overhead:

require 'table.pl';
while (<DATA>) { &table'simple($_, *array)  } ;	
__END__
key1	value1
key2	value2

this works really nice, because I don't have to worry about getting
the syntax of the associative array down exactly right in every
pass.  the table can be updated quite easily just by appending to
the file rather than inserting something in the middle too.  and
since I have a couple of tables that are the same, the code can sit
in a package off somewhere else.

table.pl looks like this, with various other types of table handling
(multiple keys, multiple values, escaping stuff in, etc) defined as
I need them.

# table.pl.  load in various types of tables.
package table;

sub table'simple {
	local($_, *ary) = @_;
	if (!/^[ \t]$/) {
		($key,$value) = split;
		$ary{$key} = $value;
	}
	1;
}

thanks.

--Ed