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

I have some code that stems from another node. I revised my code to store my data in a hash.

When it comes down to looping through my array of hash keys in a "foreach" loop for the @hiddenKeys array, my $_ variable looks like it's printing the whole array and not just the list element being evaluated.

In this code the output should incrementally add hidden fields for the "city" and "distance" form fields every time the CGI script is posted. So if you submit 5 times you should have 10 (2 form elements in the form) hidden fields. After 3 submissions there should be 6 hidden fields with names: city1, city2, city3, distance1, distance2, distance3.

What am I doing wrong?

Here's my code:
#!/usr/bin/perl -w use CGI::Carp qw(fatalsToBrowser); use strict; use CGI; use Data::Dumper; my $cgi = CGI->new(); my %cgiParam = $cgi->Vars; # Store CGI params into a hash my @cgiParamKeys = keys %cgiParam; # Store the %cgiParam keys into an +array my @cityKeys; my @distanceKeys; foreach( @cgiParamKeys ) { if( /city/ ) { #If a hidden city field found store it in an array; push @cityKeys, $_; #Store the field in an array @cityKeys = sort @cityKeys; #Sort the array } if( /distance/ ) { #If a hidden distance field found, store it in an + array push @distanceKeys, $_; #Store it in the array @distanceKeys = sort @distanceKeys; #Sort the array } } my $numLocations = @cityKeys; #Store the last city and distance submited as the last City and distna +ce element in the hash $cgiParam{"city$numLocations"} = $cgiParam{'postCity'}; $cgiParam{"distance$numLocations"} = $cgiParam{'postDistance'}; ### #Store new keys into array used for printing hidden keys push @cityKeys, "city$numLocations"; push @distanceKeys, "distance$numLocations"; ### my @hiddenKeys = qq(@cityKeys @distanceKeys); print $cgi->header; print Dumper %cgiParam; print "<br><br>"; print Dumper @hiddenKeys; print $cgi->start_form(-action=>'help.pl'); foreach( @hiddenKeys ) { if(/city/) { print $cgi->hidden("$_", "$cgiParam{$_}"); } if(/distance/) { print $cgi->hidden("$_", "$cgiParam{$_}"); } } print <<EOF; <select name="postCity" tabindex="1"> <option value="1">Columbus</option> <option value="2">Chicago</option> </select> &nbsp&nbsp&nbspDistance:&nbsp<input type="text" name="postDistance" si +ze="6" maxlength="6" /> <br> EOF print $cgi->submit('Go'); print $cgi->end_form;

Replies are listed 'Best First'.
Re: Trouble printing default variable in "foreach" loop.
by pg (Canon) on Sep 14, 2005 at 03:45 UTC

    Your main problem is with this one line:

    my @hiddenKeys = qq(@cityKeys @distanceKeys);

    What it should be is:

    my @hiddenKeys = (@cityKeys, @distanceKeys);

    With your orginal code, you are not merging those two arrays, but rather merge everything in those two arrays into one string. Try this:

    use Data::Dumper; my @a = (1,2); my @b = (3,4); my @c = qq(@a @b); print Dumper(\@a); print Dumper(\@b); print Dumper(\@c);

    This gives you an array with one element, and that single element is '1 2 3 4'.

    Later when you loop through @hiddenKeys, you got confused and thought "like it's printing the whole array and not just the list element being evaluated". That was just your misunderstanding of the debug output. You have only one hiddenKey and that is a concatenated string of all the keys, which looked like the entire array.

Re: Trouble printing default variable in "foreach" loop.
by Tanktalus (Canon) on Sep 14, 2005 at 03:20 UTC
    my @cityKeys; my @distanceKeys; foreach( @cgiParamKeys ) { if( /city/ ) { #If a hidden city field found store it in an array; push @cityKeys, $_; #Store the field in an array @cityKeys = sort @cityKeys; #Sort the array } if( /distance/ ) { #If a hidden distance field found, store it in an + array push @distanceKeys, $_; #Store it in the array @distanceKeys = sort @distanceKeys; #Sort the array } }

    First off, you don't need to sort as you're going - sort it after you've grabbed the items you're interested in. A simpler version might be:

    my @cityKeys = sort grep { /city/ } @cgiParamKeys; my @distanceKeys = sort grep { /distance/ } @cgiParamKeys;
    That said, I doubt that sorting them is important at all. Next, your "@hiddenKeys". First off, you're creating a double-quoted (qq) string that is all the city and distance keys. That's not what you want. You want my @hiddenKeys = (@cityKeys, @distanceKeys); although you don't even need that. You can just loop through them later:
    foreach (@cityKeys, @distanceKeys)
    Inside this loop, you can combine the two conditionals to:
    if (/city/ || /distance/)
    But even then, your list of city and distance keys will always match, and you'll always want to output them, so you can just skip the conditional:
    foreach (@cityKeys, @distanceKeys) { print $cgi->hidden($_ => $cgiParam{$_}); }
    That said, you could continue to simplify the whole thing by creating a "city hash" that only contained city and distance keys, then you could dump this out even easier. But that's something I'll let you think about after absorbing this far. :-)

    (PS: ++ for the progress - good job.)

Re: Trouble printing default variable in "foreach" loop.
by GrandFather (Saint) on Sep 14, 2005 at 03:24 UTC

    If you cut out all the current diagnostic dumps and the CGI dependent stuff you get something like the following:

    use warnings; use strict; my %cgiParam = ( hiddenCity=>'1', borg=>'2', foocity=>'3', hiddendistance=>'4', bork= +>'5', fooDistance=>'6', citydistance=>'7' ); my @cgiParamKeys = keys %cgiParam; # Store the %cgiParam keys into an +array my @cityKeys; my @distanceKeys; foreach( @cgiParamKeys ) { if( /city/ ) { #If a hidden city field found store it in an array; print "city: $_\n"; push @cityKeys, $_; #Store the field in an array @cityKeys = sort @cityKeys; #Sort the array } if( /distance/ ) { #If a hidden distance field found, store it in an + array print "distance: $_\n"; push @distanceKeys, $_; #Store it in the array @distanceKeys = sort @distanceKeys; #Sort the array } } my @hiddenKeys = qq(@cityKeys @distanceKeys); foreach( @hiddenKeys ) { if(/city/) { print "$_"; } if(/distance/) { print "$_"; } }

    Which prints:

    city: citydistance distance: citydistance distance: hiddendistance city: foocity citydistance foocity citydistance hiddendistancecitydistance foocity c +itydistance hiddendistance

    Does that demonstrate the problem that you have? If it does, can you describe the problem in the context of this example. If it doesn't, can you provide a similar example that shows the problem?


    Perl is Huffman encoded by design.