http://qs321.pair.com?node_id=55323

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

am i even close here? the line with the ******** is the problem. i'm trying to retrieve each row that matches $facvalue and push to one array.
my $facvalue = param('name'); my $facrowid = $ARGV[0] || $facvalue; my %facdata = (); my @facfields = split(/\t/, <FILE>); chomp @facfields; while(<FILE>) { chomp; my @facrow = split(/\t/); if ($facrow[0] eq $facrowid) { ******push @facdata{@facfields} = @facrow; ******* last; } } close (FILE);

Replies are listed 'Best First'.
references quick reference (Re: push)
by tye (Sage) on Jan 31, 2001 at 00:09 UTC
    • To use a reference, say $ref, you put {$ref} in place of a variable name (not counting the $, %, or @):
      $scalar ${$sRef} @array @{$aRef} $array[0] ${$aRef}[0] $#array $#{$aRef} %hash %{$hRef} $hash{KEY} ${$hRef}{KEY} @hash{@list} @{$hRef}{@list}
    • If the reference is held in a simple scalar variable, then the { and } can be dropped:
      $scalar $$sRef @array @$aRef $array[0] $$aRef[0] $#array $#$aRef %hash %$hRef $hash{KEY} $$hRef{KEY} @hash{@list} @$hRef{@list}
    • If you are getting a scalar from a hash or array, then you can replace ${$ref} with $ref->:
      $array[0] $aRef->[0] $hash{KEY} $hRef->{KEY}
    • If the reference is in a hash or array, then you can drop the -> between the adjacent [0] and {KEY}:
      ${$aRef->[0]}[1] $aRef->[0]->[1] $aRef->[0][1] ${$aRef->[0]}{KEY} $aRef->[0]->{KEY} $aRef->[0]{KEY} ${$hRef->{KEY}}[1] $hRef->{KEY}->[1] $hRef->{KEY}[1] ${$hRef->{A}}{B} $hRef->{A}->{B} $hRef->{A}{B}

    I hope that makes things much clearer.

            - tye (but my friends call me "Tye")
      And if I want to ref the entire hash/array (as in a foreach loop) what's the syntax?
      foreach my $array_elem ( @{ $href->{$key} } ) {
      works (I hope) but shouldn't there be a:
      foreach my $array_elem ( $href->{$key}->@ ) {
      sort of way to do this?

      a

        I like the option you describe and I recall patches to implement that making it to p5p but I'm not sure whether they got accepted nor which version of Perl they will appear in.

                - tye (but my friends call me "Tye")
      thanks. i think i better get some cafeine and do some studying.
        make that caffeine.
Re: push
by arturo (Vicar) on Jan 30, 2001 at 23:58 UTC
    facdata is a hash, so push isn't appropriate. You don't need to push anything onto the hash; near as I can tell, what you want to do is to set the keys of %facdata as the values of @facfields, and the corresponding values to the values in @facrow

    You're very close indeed :

    $facdata{@facfields} = @facrow (a hash slice) seems to be what you want.

    Philosophy can be made out of anything. Or less -- Jerry A. Fodor

      i need to keep search the rows because there will be more than one with the value i'm looking for. can i do a while and a foreach in the same statement?

        If you may have more than one value that you want to save, I'd use an array of (references to) hashes; at least, sticking with the structure you've already got. For that, you're going to need to read up on references, so perldoc perlref and perldoc perlreftut are your starting points. something like the following should get you started:

        my @lines; # ... as before # instead of *your* push line, @facdata{@facfields} = @facrow; push @lines, \%facdata;

        That will leave you with an array of references to hashes. This is a little hairier than the structure you might want (you could, e.g. just store an array of strings, and call the splits on those later. But whatever ...)

        Philosophy can be made out of anything. Or less -- Jerry A. Fodor

      the thing without the push, as you said, it's working fine, except that i need it to continue through the rows, and get each row that matches. i don't know how to do that (it seems like using foreach is the thing, but can't get it to work), or where to put the rows - push them all into one array, or into different arrays - but then i don't know how to name them.

      i've looked through all my reference books, and online, and can't find anything exactly like this. either i just don't know the syntax well enough or i'm approaching it wrong. for example, should i make this a sub routine because of the my statements?

      thanks for the help.

        I'm not seeing the problem, unless it has to do with that last at the end of the conditional within the loop that traverses the file. The way your code above is written, you will only ever get one entry, because the last breaks out of the while.

        Conceptually, it seems you want a list of the lines in the file that match your criterion, so you definitely should use a single array to hold them all. The way you've written things so far, you want to store those lines not as simple strings (which may or may not be what you should do, depending on the overall aim) but as hashes. Perl doesn't directly support multi-dimensional arrays, or arrays of hashes, but it lets the programmer do it by giving you the ability to store references to any kind of variable any place where you can use a scalar.

        To figure out what the implications of that are, and how to use the tools Perl gives you, you're going to need to consult the documentation I pointed to in my posts above. The basic idea: arrays hold scalars; references are a special type of scalar -- you can think of them as arrows that point to data structures (such as hashes), so what you need to do when you've got a value that is a reference is to get at the thing the reference points to; there are a number of ways of doing that, but here's not the place to duplicate the documentation you have available to you.

        To perform an operation on every element of an array (such as printing out the elements -- NOTE this won't work as you expect if the members of the array are references) , you will almost always want to use foreach.

        HTH

        Philosophy can be made out of anything. Or less -- Jerry A. Fodor

(tye)Re: push
by tye (Sage) on Jan 31, 2001 at 04:19 UTC
    mapcar { push @{$facdata{$_[0])}, $_[1] } \@facfields, \@facrow;

    Is that more what you wanted? If you don't want to use mapcar (type mapcar in the PM Search box to find it), then you can code that like:

    for( 0..$#facfields ) { push @{ $facdata{$facfields[$_]} }, $facrow[$_]; } # or push @{ $facdata{$_} }, shift @facrow for @facfields;

    Hope that helps.

            - tye (but my friends call me "Tye")
Re: push
by malaga (Pilgrim) on Feb 01, 2001 at 05:27 UTC
    i posted this question for tilly but i should have posted it here so everyone can see it. please excuse the duplication
    i have been trying to tweak this script to cover one more aspect of what i am doing - i need to search for all rows that have a particular id, not just the first one. here's where the script is:
    open FILE , "/courses.txt" or die "Cannot open: $!"; my $couvalue = param('name'); my $courowid = $ARGV[0] || $couvalue; my %coudata = (); my @coufields = split(/\t/, <FILE>); chomp @coufields; while(<FILE>) { chomp; my @courow = split(/\t/); if ($courow[0] eq $courowid) { @coudata{@coufields} = @courow; last; } } close (FILE); print header; if ( keys %coudata ) { # found, display data print ul( map { li("$_: $coudata{$_}") } keys %coudata); } else { # not found, show error print h1("Can't find row '$courowid'"); } #######this is what the table looks like: 1414 "Nutitional Epidemiology" 1414 "Nutritional assessment" 1414 "Undernutrition in the United States" 1371 "Health Politics and Policy" 1371 "Advanced Health Politics?" 1371 "Introduction to Health Policy & Management" 1371 "Health and Public Policy Seminar"
    i only get one row back.
    i've tried taking out "last;"
    i've tried to do a foreach instead of an if, but i can't get it to work. i've even posted another sopw, in trying to use "push" to get all the lines that match into one array. can this script work for this problem, and if so, where should changes be made?

    thank you for your help.
    malaga
      Start w/: use strict, use perl -w. Trust us, it'll really help you here. But, enabler that I am: you're mixing up your arrays and hashes. You're also trying to read FILE twice:
      my @coufields = split(/\t/, <FILE>);
      should eat all of FILE, leaving nothing left for the while loop. Leave that line out and try:
      my $debug = 5; print STDERR "Looking for id $courowid \n" if $debug > 8; while (<FILE>) { chomp; my ($id, $course) = split(/\t/); print STDERR "trying id $id, course $course\n" if $debug > 8; if ( $id == $courowid ) { print STDERR "found id $id, course $course\n" if $debug > 5; # if we match the ID, push the course name onto the # the array in the coudata hash, key $id push @{$coudata{$id}}, $course; } } print header; if ( %coudata ) { # found, display data by taking each course back outof # the array foreach my $id ( keys %coudata ) { print STDERR "show id $id\n" if $debug > 6; foreach my $course ( @{$coudata{$id}} ) { print STDERR "show id $id, course $course\n" if $debug > 8; print ul("$id: $course"); } # foreach my course } # foreach my id }else { # not found, show error print h1("Can't find row '$courowid'"); }
      Its a big step in perl, but names are only half the battle: %coudata and $coudata{14146} are related but the first is the entire hash, the latter a single element found by key '14146'. @coudata is an entirely different kettle of data, its an array and the element $coudata[0] has no relation to the hash element $coudata{0}; just as @coudata doesn't have any relation to %coudata.
      Plus, your variable names are troublesome: use english-ish things: course_list, course_id, search_id etc. You'll avoid typos (courdata coudata) and it'll make more sense. Just 'cause you can, doesn't mean its a good idea to to name an array and a hash the same thing. Its generally a bad idea, esp. early on. A few more keystrokes won't kill ya'

      a

        thanks a, this was really helpful. - malaga