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

i'm using a multidimensional array. it has to support a variable number of dimensions. currently, i'm doing it with code specific to each dimension. it's brute-force and want to know if is there another way to do this ?
my $ranges = [ { I => "2", }, { I => "3", J => "2", }, { I => "4", J => "3", K => "2", } ]; foreach my $rvarHASH ( @{ $ranges } ) { my @rvarLIST = sort keys %$rvarHASH; my $array; my ($ii,$vi,$ij,$vj,$ik,$vk); if ( scalar @rvarLIST == 1 ) { $ii = $rvarLIST[0]; ## index name $vi = $rvarHASH->{$ii}; ## index value $array->[$vi] = $netlist; } elsif ( scalar @rvarLIST == 2 ) { $ii = $rvarLIST[0]; $vi = $rvarHASH->{$ii}; $ij = $rvarLIST[1]; $vj = $rvarHASH->{$ij}; $array->[$vi]->[$vj] = $netlist; # hardcoded } elsif ( scalar @rvarLIST == 3 ) { $ii = $rvarLIST[0]; $vi = $rvarHASH->{$ii}; $ij = $rvarLIST[1]; $vj = $rvarHASH->{$ij}; $ik = $rvarLIST[2]; $vk = $rvarHASH->{$ik}; $array->[$vi]->[$vj]->[$vk] = $netlist; } else { print "\nmore than 3 dimensions not supported yet \n\n"; }
i tired to create the Ndim array first and then add its reference to the main data structure. but failed miserably. thanks

Replies are listed 'Best First'.
Re: multidimensional arrays
by naikonta (Curate) on Jun 10, 2007 at 04:56 UTC
    Use Data::Dumper to check your data structure and see if that's what you want. You're somewhat unclear here about what you mean by "failed" since you don't tell us what you expected and what you got.

    It smells like the $array you build will go nowhere since it's declared with my inside the foreach loop but never accessed. Also, are using strict and warnings? See also I know what I mean. Why don't you?.


    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

      the code is currently working and i've used data::dumper to confirm my data structure. $array is used later in code not shown as it is not relevant to the question. it sounds like you don't have an alternative to hardcoding the dimensions, either! that's good to know, maybe my solution is the only way....
        $array is used later in code not shown as it is not relevant to the question.
        How that array would be used or accessed, is obviously very relevant to how it would be built. Yes, your code seems fixable, but it's not impossible that your code had to be rewritten (so it's more elegance, or faster, or more readable, or more efficient, whatever your goal) if we knew more information about what it's prepared for.
        it sounds like you don't have an alternative to hardcoding the dimensions, either!
        Oh, I'm sorry to dissapoint you. Just because I don't provide it here doesn't mean I don't have one. The things is, I refused to do what you might expected me to do due the reason I gave here. I'm not as wise as many monks here, but I'm free to choose how or what I reply, and you're free to choose any reply that most suits you, as well.

        And why do you think my reply was solely to solve your problem? The reason I posted here is so we (and it's not just you and me) can all learn from each other, both our mistakes and rightness. That's what, as far as I understand it, a community-based forum is for.

        Immediate update: I forgot to say that.... Nevertheless, you actually present a pretty interesting situation I upvoted the node.


        Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

        the code is currently working and i've used data::dumper to confirm my data structure. $array is used later in code not shown as it is not relevant to the question. it sounds like you don't have an alternative to hardcoding the dimensions, either! that's good to know, maybe my solution is the only way....

        Well, for one thing it's important to always remind that true multidimensional arrays do not exist (yet). Re dimensions, it's not strictly true that you have to hardcode them. Indeed D::D and similar modules themselves are the proof that it is possible not to. You have to navigate through the structure. Either recursively or possibly iteratively, as a recent and extremely interesting post shows. (But the technique shown there has to do with cloning and thus may be more complex than you actually need.) It all really depends on what you really want to do.

Re: multidimensional arrays
by BrowserUk (Patriarch) on Jun 10, 2007 at 07:56 UTC

    It would be almost a fast, use less memory (if your multi-dimensional array is sparsely populated, *much less*), and far simper to code if you use a hash with composite keys instead.

    Basically, you concatenate your indices together, separated by some suitable character (I've used ';' below), and use the result as the key to a hash. That will give you the same ability to look things up by index, but will not require you to reserve space for elements you do not use.

    Your entire snippet above can be reduce to the following, with the addition that it will handle any number of dimensions.

    my $ranges = [ { I => "2", }, { I => "3", J => "2", }, { I => "4", J => "3", K => "2", } ]; my $array; ## The name is a lie!! foreach my $rvarHASH ( @{ $ranges } ) { my @rvarLIST = sort keys %$rvarHASH; my @orderedIndices = @{ $rvarHASH }{ @rvarLIST }; my $array->{ join ';', @orderedIndices } = $netlist; } ## Err...That's all folks!

    It's a little worrying that your declaration my $array; is inside the loop, meaning that it will vanish as soon as you exit the loop, before you get a chance to use it--but I'll put that down to you being a non-strict person, that added a few mys to avoid the cacophony :)


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      i wanted to use a multidimensional array instead of composite key hash because i have to acess it many times. its easier to access the array once with the N indices than looping through all the keys and deconstructing them for each access.

      gee, i apologize for hoopla about the $array inside the loop. the $range hash and the outermost loop was put in solely for the question! it's hard to distill ones code for outside review.

      thanks for your comments.

        i wanted to use a multidimensional array instead of composite key hash because i have to acess it many times. its easier to access the array once with the N indices than looping through all the keys and deconstructing them for each access.

        You'll have to explain what you mean by that? It's not at all clear to me why you would need to "deconstruct" the hash keys to access them?


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: multidimensional arrays
by shigetsu (Hermit) on Jun 10, 2007 at 06:04 UTC

    Glaring at your code, I can see (or so I think) what you're trying to achieve, but I'm asking myself whether you're using an appropriate datastructure though.

    Multidimensional arrays are fine, as long as you *really* need them (for example, if you're reliant upon indices and order). But let's examine some code first:

    $array->[$vi]->[$vj]->[$vk] = $netlist;
    If I understand correctly $array->[$vi]->[$vj]->[$vk] resolves to $array->[4]->[3]->[2], agreed? You could be using a 'multidimensional' hash here, because as I understand it, you're not utilising the array indices properly, but rather should have hash keys to lookup items. Should you have the risening need, to have multiple items attached to a key, you could work with a Hash of Hashes of Arrays, for example.

    You might find Hash::Flatten appealing as a start.

    Furthermore, I don't think, it's worth pointing out minor nits as long as the overall concept doesn't fit in nicely.

      you understand my code correctly! i used the hash $range just for this question.

      i need to access data by index. The number of indices is a variable.

      array->[4] array->[3]->[5] array->[7]->[2]->[3]
      are all valid.

      interestingly, the problem is CREATING the Ndim array in a general manner. most examples show loops to build each dimension, each nested loop is one dimension. i actually did write this first, and entering the next nested loop was predicated on a valid index. however, i still have to have N nested loops to support N dimensions. i used the "scalar" paradigm for my question as i thought it is more comprehensible.

      i found it straightforward to ACCESS the array generally with the use of "ref". (if the reference is ARRAY, it's another dimension of the array!)

        interestingly, the problem is CREATING the Ndim array in a general manner. most examples show loops to build each dimension, each nested loop is one dimension.

        I don't really think so. It all really depends on how you want to fill it. For example, suppose you just want to create a n-dimensional m x m x ... x m array(-ref) filled with 1's. Then it can be just as simple as in the following example:

        Just as obviously, I wouldn't use anything like that in a real situation, but it was just to convey an idea...

      If I understand correctly $array->[$vi]->[$vj]->[$vk] resolves to $array->[4]->[3]->[2], agreed? You could be using a 'multidimensional' hash here, because as I understand it, you're not utilising the array indices properly, but rather should have hash keys to lookup items.

      Well it really depends on how sparse his values are. The fact being that as you correctly hint by quoting, 'multidimensional' hashes are not really, either. One advantage of them over the other approach is that of not incurring in references which could require cloning.

Re: multidimensional arrays
by hangon (Deacon) on Jun 10, 2007 at 07:41 UTC

    You should be able to build the array to as many dimensions as you want using recursion. I don't quite understand what you're doing (its also late and my brain is rebelling), but here is some incomplete code to give you the general idea.

    my $ranges = [ { I => "2", }, { I => "3", J => "2", }, { I => "4", J => "3", K => "2", } ]; foreach my $rvarHASH ( @{ $ranges } ) { my @rvarLIST = sort keys %$rvarHASH; my $depth = scalar @rvarList; my $array = BuildArray(\@rvarLIST, $depth); # do whatever with $array ... } sub BuildArray{ my ($rvarLISTref, $depth) = @_; my $arrayref; $depth--; if ($depth > 0){ # last dimension not reached, so do another recursion my $rtn = BuildArray($rvarLISTref, $depth); # get index, set ref to next dimension in array $ii = ... $arrayref->[$ii] = \$rtn; }else{ # arrived at last dimension, # get index, set value into array $ii = ... $arrayref->[$ii] = $netlist; } return $arrayref; }
      thanks. i understand this code. i'll try it out.

      it's interesting. i got two approaches to do it. they vary in which direction to build it :

      from left to right : use recursion from right to left

      i focused solely on the left->right approach; but did not try recursive.

      and i simply did not even think about building it right to left.

      thanks for all the comments. this place is great!

Re: multidimensional arrays
by almut (Canon) on Jun 10, 2007 at 07:37 UTC

    When I dump the $array at the end of every loop iteration, I get the following data structures (I assigned $netlist some value, so one can tell it apart from undef):

    $VAR1 = [ undef, undef, 'NETLIST' ]; $VAR1 = [ undef, undef, undef, [ undef, undef, 'NETLIST' ] ]; $VAR1 = [ undef, undef, undef, undef, [ undef, undef, undef, [ undef, undef, 'NETLIST' ] ] ];

    Although I agree with shigetsu that this looks kinda weird, it's hard to tell if it is, without you telling us what task you're trying to solve...

    Anyhow, the same data structure can be created more succinctly by successively chaining the arrayrefs. This would even work for any dimensionality (btw, why quote the numbers if they're going to be used as numeric indices anyway?):

    my $ranges = [ { I => 2 }, { I => 3, J => 2 }, { I => 4, J => 3, K => 2 }, { I => 5, J => 4, K => 3, L => 2 } ]; for my $h ( @$ranges ) { my $array = 'NETLIST'; for my $k (reverse sort keys %$h ) { my $a; $a->[$h->{$k}] = $array; $array = $a; } # ... print Dumper $array; }

    Output:

    $VAR1 = [ undef, undef, 'NETLIST' ]; $VAR1 = [ undef, undef, undef, [ undef, undef, 'NETLIST' ] ]; $VAR1 = [ undef, undef, undef, undef, [ undef, undef, undef, [ undef, undef, 'NETLIST' ] ] ]; $VAR1 = [ undef, undef, undef, undef, undef, [ undef, undef, undef, undef, [ undef, undef, undef, [ undef, undef, 'NETLIST' ] ] ] ];

    Update:   actually, maybe you could also just take advantage of Perl's autovivification feature? For example, the following would create the 3-dimensional one of your examples:

    $array->[4]->[3]->[2] = $netlist;

    IOW, why create the multidimensional array in advance, if Perl will create it for you as required as soon as you access it, by assigning some value, or reading some element?

      thanks. your idea of chaining the arrayrefs by working backwards is interesting. i'll try this out.

      the $netlist is actually a data structure also. part of it is populated at the time the multidimensional array is created and the rest of it is populated afterwards.

      the use of multidimensional array was attractive because the "population afterwards" code requires accessing this many times. arrays are easily accessed; hash are not.

        arrays are easily accessed; hash are not.

        I see that you solved your problem, though I'm curious as to know what you meant with this. In fact arrays and hashes are very similar beasts. The main difference from the user's POV being that the former ones have indices wich are natural numbers (but for $[, I know, I know...) and the latter ones generic strings.