http://qs1969.pair.com?node_id=403322

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

I am trying to print out a list of <option>'s from an hash of array references, but i keep getting the array reference instead of the elements of the array. Here is my code,
my @columns = ( "Gender" ); my %dataoptions = ( "Gender", ["Male", "Female"] ); for ($i = 0; $i < @columns; $i++) { print $columns[$i].': '; foreach $option ($dataoptions{$columns[$i]}) { print '<input type="radio" name="'.$columns[$i].'" value="'.$optio +n.'"> '.$option.' '; } }
The output from this code is;
Gender: <input type="radio" name="Gender" value="ARRAY(0x1bba2e4)"> AR +RAY(0x1bba2e4)
Can anyone help me out here? Thanks.

Replies are listed 'Best First'.
Re: Foreach in a 2D array
by ihb (Deacon) on Oct 28, 2004 at 09:15 UTC

    You need to dereference the array before iterating through it, otherwise you iterate over one element (the reference). That means you need to change $dataoptions{$columns[$i]} to @{$dataoptions{$columns[$i]}}.

    Related documents:
    perlref - Perl references and nested data structures
    perlreftut - Mark's very short tutorial about references
    perllol - Manipulating Arrays of Arrays in Perl
    perldsc - Perl Data Structures Cookbook

    ihb

    See perltoc if you don't know which perldoc to read!
    Read argumentation in its context!

Re: Foreach in a 2D array
by tachyon (Chancellor) on Oct 28, 2004 at 10:01 UTC

    You are close:

    foreach $option ($dataoptions{$columns[$i]}) { print "We can deref all like this : @$option\n"; printf "Deref array elem->0 : %s and elem->1 : %s\n", $option->[0] +, $option->[1]; }

    cheers

    tachyon

Re: Foreach in a 2D array
by hmerrill (Friar) on Oct 28, 2004 at 12:31 UTC
    Remember that a hash is a datastructure where each element has a *key* and a *value*. Looking at your code I think your intent was to create a hash (%dataoptions) that had one key "Gender", and that key "Gender" having a related value that is an array whose elements are "Male" and "Female".

    So, this:

    my %dataoptions = ( "Gender", ["Male", "Female"] );
    should be this:
    my %dataoptions = ( "Gender" => ["Male", "Female"] );
    The other thing to remember is that this:
    ["Male", "Female"]
    creates a *reference* to an anonymous array. So in hash %dataoptions, the value corresponding to key "Gender" is a *reference* to an anomyous array containing elements "Male" and "Female". So then when you do
    foreach $option ($dataoptions{$columns[$i]})
    $option is a *reference* to an array.

    I think you want your loop to be something like this:

    for ($i = 0; $i < @columns; $i++) { print $columns[$i].': '; my $arrayref = $dataoptions{$columns[$i]}; foreach $option (@$arrayref) { print '<input type="radio" name="'.$columns[$i].'" value="'.$ +option.'"> '.$option.' '; } }
    HTH.

      This is probably just a minor nit, but thought I might learn something. My understanding is that there is very little difference (from the perl interpreter standpoint) between:

      my %hash = ('a', 'b', 'c', 'd');
      and
      my %hash = ('a' => 'b', 'c'=> 'd');

      Advantages I can think of for the first representation are:

      • As long as you know that a list has an even number of elements, you can use it as a hash.

      The advantages I can think of for the second representation are:

      • The => reads better from a maintainability standpoint
      • you don't have to quote the key. For example:
        my %hash = (a=>'b', c=>'d');

      Essentially => is syntactic sugar for ,.

      Update. Missed a tic.


      "Look, Shiny Things!" is not a better business strategy than compatibility and reuse.


      OSUnderdog
        You're right. Sorry, I was a little quick to correct the "," to "=>", but I think the comma works fine as you pointed out. I'm so used to using "=>" that at first glance I thought using the comma was illegal. The "as long as you know that the list has an even number of elements..." part is enough for me to use the 2nd version ("=>") since I know the 2nd one works all the time :-) In fact all the perl documentation on hashes shows the "=>" operator.

        As I think you'll agree, in this code:

        my %hash = ('a', 'b', 'c', 'd');
        it is not clear that 'a' is the key referring to value 'b'.
Re: Foreach in a 2D array
by geekgrrl (Pilgrim) on Oct 28, 2004 at 15:48 UTC

    I have two suggestions - one answers your question and the other makes it easier to read that print statement.

    1. foreach $option (@{$dataoptions{$columns[$i]}})
      This dereferences your array - it is just a way to do it without having to save it to another variable first.
    2. print qq(<input type="radio" name="$columns[$i]" value="$option">$opti +on);
      This makes it easier to read the HTML tag.
Re: Foreach in a 2D array
by ihb (Deacon) on Oct 29, 2004 at 15:57 UTC

    Now that you've gotten answers to your question, I figured I'll answer the real question and give an answer that you can't easily read yourself to about data structures.

    The first and most interesting part is to redesign the data structure. It seems that you want an ordered list and therefore have an array with the columns, but you also want to have several values attached to that value. My suggestion is that you consider having a data structure looking like

    my @data = ( [ Gender => [ 'Male', 'Female'] ], [ A => [ 'a', 'b', 'c'] ], );
    which would remove the need for parallel data structures. Your code would then look like
    for (@data) { my ($column, $options) = @$_; print $column . ': '; foreach my $option (@$options) { print '<input type="radio" name="' . $column . '" value="' . $ +option . '"> ' . $option . ' '; } }
    As a side note, I think that print statement is more clearly written using printf() and another qoute delimiter:
    printf qq{<input type="radio" name="%s" value="%s">%s\n}, $column, $option, $option ;
    But really, this is unnecessary. CGI already implements a &radio_group subroutine that we can use and get rid of the loop altogether.

    The end result is

    use strict; use CGI; my $q = CGI::->new; my @data = ( [ Gender => [ 'Male', 'Female'] ], [ A => [ 'a', 'b', 'c'] ], ); for (@data) { my ($column, $options) = @$_; print $q->radio_group( -name => $column, -values => $options, -default => '_', ); # '_' is chosen as default as that will make # no value checked by default, as long as you # don't have '_' as an option. }

    If you at any time would like to do a lookup of which values that are accepted for a radio group you can degenerate your @data to %dataoptions by using

    my %dataoptions = map @$_, @data;

    Related documents:
    perlref - Perl references and nested data structures
    perlfunc - Perl builtin functions (look for map)
    CGI - Simple Common Gateway Interface Class

    ihb

    See perltoc if you don't know which perldoc to read!
    Read argumentation in its context!

      Thanks for the tips on the data structure, but the reason i have it like that is because there is multiple other arrays and hashs that are related to the columns array that i dont neccesarily call by name. They got deleted out when i was trying to simplify the script so that people would not have to worry about the rest of it. Thanks anyway.