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

Hello Monks,

I will put forward my questions with a simple example.

For eg: I have a hash, which has many keys, like xx,yy,zz. Each of the keys point to an array of hashes. ( i get this structure from the result of reading a JSON file and converting into perl ).

My Current design, i used to create individual arrays named @xx, @yy, @zz to store their corresponding values for each of the keys xx, yy, and zz.

Now i have encountered a problem where if somewhere were to add a new key called "aa". They would have to manually edit the code to create an array called @aa to store its values.

How do i avoid this? It was stupid of me not to think of this before i started designing the project.

I would like to design it such that no matter hw many keys are there, their values are read and stored correspondingly in arrays, which are created dynamically.( im sorry if im using the wrong words, but u get my problem ).

Im guessing its got something to do with hashes and anonymous arrays? :-/ Please enlighten.

Thanks!
  • Comment on Question with Dynamically Creating arrays.

Replies are listed 'Best First'.
Re: Question with Dynamically Creating arrays.
by kennethk (Abbot) on Jul 21, 2010 at 16:10 UTC
    Given that you are handling a complex data structure in Perl (perldsc, perllol, perlreftut) why do you feel the need to have the values stored in another array? Assuming you have a hash %base that is perhaps a deference of the hash ref returned from your JSON parse, you could access deep data with syntax similar to $base{xx}[1]{key1} (the value corresponding to key1 in the second referenced array corresponding to xx). You can loop over all these structures, store local copies of the arrays/hashes using references to reduce boiler plate and anything else you'd normally do in Perl.

    You might also find a read through of Why it's stupid to use a variable as a variable name enlightening, though it is a little off point as you are (thankfully) not using symbolic references.

    I'd love to give more concrete advice, but without any source code to critique (How do I post a question effectively?) I'm limited in suggestions. Post some source code, and we'll be happy to give you some ideas on how to improve it (subjective advice, of course).

      Hello Kenneth,

      Im sorry but i will not be able to post any code. I hope you understand. :(

      I will give u a clearer picture of what i am doing, and what i want. I hope it helps.

      I am able to access the elements using the hash ref. like what u said. $base{$key}index{$inner_key} is what i have done.

      For Eg;

      $base{$key}index{$inner_key} contains shell commands, like ls, ps, who, etc etc.

      I want to be able to store the outputs of each of these commands ( $inner_key ) in an array which is specific to the outer hash key. ( $key ).

      like $base{"xx"}[0]{"ls"} $base{"xx"}1{"ps"}

      $base{"yy"}[0]{"top"} $base{"yy"}1{"finger"}

      i want each of those outputs to be stored in an array called @xx and @yy where $xx[0] = "output of ls" $xx1 = "output of ps"

      $yy[0] = "output of top" $yy1 = "output of finger"

      Is there a way i can handle this independent of the $key value/name.

      i was thinking of creating something on the lines of $somestruct{$key} = [] // anonymous array. and then i can push or pop values into the anony array. I have no idea if what im asking is even valid. Im actually confused now..

        Please wrap code in <code> tags - see Writeup Formatting Tips. Because you did not wrap your code in code tags, many of your [ ] pairs indicating array indices were linkified.

        I understand not being able to post the vast majority of a given code, but demonstration code and pseudo-code tends to be worth 1k words.

        I'm a little confused by your data structure. You wrote $base{"xx"}[0]{"ls"} (HoAoH) without specifying its value, but it sounds more like you have $base{"xx"}[0] = "ls" (HoA) from your description. For the sake of this discussion, I will sort of split the difference and assume something like $base{"xx"}[0]{"ls"} = "ls xx" (HoAoH).

        If you are committed to having an array named @xx, then you need to introduce the new variables you say you are trying to avoid. However, a more natural solution (and fairly close to how I construe your last comment) would be to store the a second hash of arrays (HoA). Something like

        #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my %base = (xx => [{ls => 'ls xx 2>&1'}, {ps => 'ps xx 2>&1'}, ], yy => [{top => 'top yy 2>&1'}, {finger => 'finger yy 2>&1'}, ], ); my %output; for my $key (keys %base) { for my $element (@{$base{$key}}) { my $inner_key = (keys %$element)[0]; my $command = (values %$element)[0]; my $result = `$command`; push @{$output{$key}}, $result; } } print Dumper \%output;

        This should at least give you an idea of how to avoid your naming issue. If anything is unclear, I'll be happy to clarify.

        What you're now describing sounds like a dangerous path to go down, especially as far as debugging and code maintenance is concerned. At least it would be for me. :)

        Based on you new post, it sounds like you have one complex data structure that is storing commands and you want store the output of those commands. If that's correct, why not store both the command and output in the same data structure? (See example code, based on you last post, below)

        my %base; $base{"xx"}[0]{"command"} = "ls"; $base{"xx"}[0]{"output"} = `$base{"xx"}[0]{"command"}`;

        If you go this route, you'll avoid using a variable's value as the name of a variable. Plus, if you use a module like Data::Dumper when debugging, it'll help you see easily and quickly what the command was along with it's output. Hope this helps.

        Why do you want to go through so much trouble to avoid putting the output in a similarly configured hash?  $output{xx}[1] seems like a small price to pay to avoid using the symbolic references you seem to be talking yourself into.

      Hello Kenneth,

      Im sorry but i will not be able to post any code. I hope you understand. :( I will give u a clearer picture of what i am doing, and what i want. I hope it helps. I am able to access the elements using the hash ref. like what u said. $base{$key}index{$inner_key} is what i have done.

      For Eg;

      $base{$key}index{$inner_key} contains shell commands, like ls, ps, who, etc etc.

      I want to be able to store the outputs of each of these commands ( $inner_key ) in an array which is specific to the outer hash key. ( $key ).

      like $base{"xx"}[0]{"ls"}
      $base{"xx"}1{"ps"}

      $base{"yy"}[0]{"top"}
      $base{"yy"}1{"finger"}

      i want each of those outputs to be stored in an array called @xx and @yy where
      $xx[0] = "output of ls"
      $xx1 = "output of ps"

      $yy[0] = "output of top"
      $yy1 = "output of finger"

      Is there a way i can handle this independent of the $key value/name.

      i was thinking of creating something on the lines of $somestruct{$key} = [] // anonymous array. and then i can push or pop values into the anony array. I have no idea if what im asking is even valid. Im actually confused now..

        sorry. i reposted it by mistake.. will have a look at your replies.
        Thanks
Re: Question with Dynamically Creating arrays.
by Anonymous Monk on Jul 21, 2010 at 16:09 UTC
Re: Question with Dynamically Creating arrays.
by biohisham (Priest) on Jul 21, 2010 at 18:12 UTC
    Now i have encountered a problem.....They would have to manually edit the code to create an array called @aa to store its values.

    If I understood this well enough, you will add the keys manually to the hash right?, you can do that directly without having to explicitly create an array every time to hold the values for you. That other array holding the key names can be dynamically updated for hash keys by simply assigning to the array the keys of the hash using the keys function...

    #!/usr/local/bin/perl use strict; use warnings; my %hash; my @array_keys; my @keys = qw(xx yy zz); @hash{@keys}=([{'key1'=>1}],[{'key2'=>2}],[{'key3'=>3}]); #hash slice @array_keys = keys %hash; print "@array_keys\n"; push @{$hash{'aa'}},{'key4'=>4}; #add another key @array_keys = keys %hash; #update the array print "@array_keys\n";
    UPDATE:This post is a response to the OP original post which did not bear a mention of the requirement that he presented in the response to kennethk reply to the same post.. It is possible however to integrate further approaches towards achieving the OP's desired goal, for example:
    for (@array_keys){ for my $element ($hash{$_}){ my @val; for my $element1 ($element->[0]){ @val = values %$element1; } print "@val\n"; } } #OR @array_values = values %hash; for (@array_values){ for my $element ($_){ my @val; for my $element1 ($element->[0]){ @val = values %$element1; } print "@val\n"; } }
    The following code is still welcoming improvement...
    use strict; use warnings; use Data::Dumper; my %hash; my @array_values; my @keys = qw(xx yy zz); @hash{@keys}=([{'key1'=>'ipconfig'}],[{'key2'=>'dir'}]); @array_values = values %hash; my %hash_output; for (@array_values){ for my $element ($_){ for my $element1 ($element->[0]){ for my $result (values %$element1){ my $outcome = `$result`; push @{$hash_output{command}[0]}, $out +come; } } } } print Dumper(\%hash_output);


    Excellence is an Endeavor of Persistence. A Year-Old Monk :D .
Re: Question with Dynamically Creating arrays.
by aquarium (Curate) on Jul 22, 2010 at 05:42 UTC
    you are quite correct in your assessment of the hardcoded array name code needing some re-factoring, for extensibility pursposes. i'd only consider someone stupid-actually usually just lazy-if you point out to them such a design shortcoming, and they won't bother to fix or even bother to learn. so you shouldn't be so harsh on yourself.
    other monks have pointed to solutions and possible further refactoring. it just dawned on me that some of these solutions might be a little hard to wrap one's head around sometimes. the straightforward solution is to go from @aa, @bb, @cc to $arrays{aa}, $arrays{bb}, $arrays{cc} etc. thus merely adding a hash wrapper.
    the hardest line to type correctly is: stty erase ^H
      Hello Everyone

      Thanks for all the replies. It was really useful and now I got it to work, although im yet to modify other portions of my code, which i am doing right now.
      @aquarium - what u mentioned is pretty much what i had done.
      %struct;
      i use the $key and create a hash value which i then make it point to an array. By doing this i have my structure unique to the key. something like :
      push(@{$struct{$key}},"value") or // i passed the reference n added the values to the array add(\@{$struct{$key}});
      So now $struct points to an array, and also has the key as i wanted.
      Thanks for the help everyone!
Re: Question with Dynamically Creating arrays.
by locked_user sundialsvc4 (Abbot) on Jul 26, 2010 at 22:14 UTC

    Perl’s concept of “references” can be put to very good use in situations like these, and Perl makes it very easy to do.   (In fact, it often happens implicitly.)

    Once you have constructed a single memory-variable to contain a piece of data, you can add references to it into any number of other hashes or lists ... thereby giving yourself the ability to quickly find the value using any of these means.   In the memory of the computer, there is only one instance of the value.   That instance contains a “reference count” (managed entirely by Perl), which says how many references to it have been created.   The data will not be garbage collected until that reference count becomes zero.

    In your example, it seems that when presented with a variable having a key such as xx,yy,zz, you wish to be able to find the value using any one of those subkeys.   Two approaches immediately come to mind:

    1. Just store the value in a hash under its key, then use the grep function against the list of keys which contain the substring you are looking for.   Search all of the keys that you find.
    2. Create a single record containing the key, then break down the key into its constituent parts and add a reference to the record under each part.   (The data structure in this case would be a hash pointing to an array of references.)