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

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

Learned Monks, I have constructed a two dimensional array of data and I wish to extract certain columns. I have attempted to use a slice notation thus;
my @slice = @2darray([o][3]...[25][3]); # [3] is the column I want
This form (if I recal correctly) does not cause "strict" to barf all over my console however what it does retrieve is EVERY element from [0][3] to [25][3]. So, is it possible using a slice type notation to get one column of data or am I (urm) barfing up the wrong tree? many thanks.

Replies are listed 'Best First'.
Re: Extracting columns from a two dimensional array
by Aristotle (Chancellor) on Dec 21, 2004 at 09:19 UTC

    The code you show doesn't even compile. In any case, you can't use a slice to do what you want. You need a map.

    my @col = map $_->[ 3 ], @AoA;

    Makeshifts last the longest.

      Could this work for populating a hash with 2 columns of an array or arrays? And if so, would it be more efficient than walking through the array assigning key/value pairs from the columns...as in:
      my %hash; for (my $row = 0; $row < @array; $row++) { $hash{"$array[$row][6]"} = "$array[$row][7]"; }
      Jack
      Cogito cogito, ergo cogito sum
      (I think I think, therefore I think I am)

        That is an entirely different question.

        First off: please don't use for(;;) loops in Perl unless you really need to. For iterating over arrays or lists, there is almost never a good reason to favour this type of loop. Use foreach( LIST ) instead — that will very nearly remove the source for a large chunk of off-by-one errors.

        And make it a habit not to use doublequotes where you don't need them. It will bite you when stringification is not an identity function, such as with references, besides the fact that it forces needless work on the interpreter.

        foreach my $row ( @array ) { $hash{ $row->[ 6 ] } = $row->[ 7 ]; }

        If you actually needed the index, you can do that too:

        foreach my $i ( 0 .. $#array ) { $hash{ $array[ $i ][ 6 ] } = $array[ $i ][ 7 ]; }

        Of course in this case you don't.

        As to your question, well, something very similar can indeed be done:

        my %hash = map { $row->[ 6 ] => $row->[ 7 ] } @array;

        Whether that's faster or not I don't know and don't care, and neither should you. I do know that it's not going to be measurably faster or slower either way, since both ways do roughly the same amount of work. Other factors are going to dictate the speed of your program anyway — if you read a lot of data from a file or pull any across a network, the penalty waiting for spinning metal or travelling electrons will outweigh microoptimizations like this one by so many orders of magnitude that the impact of for vs map won't be more than noise in your benchmarks. (You do benchmark, don't you?) And that's just two examples of the things that will eclipse this so much. So choose the idiom that you find easier to understand.

        • Programs must be written for people to read, and only incidentally for machines to execute. —Abelson and Sussman
        • It is easier to optimize correct code than to correct optimized code. —Bill Harlan

        Makeshifts last the longest.

      Aristotle, you're dead right - sorry about that, however I am pleased to note you got my point and provided me with the nugget I needed. thanks.
Re: Extracting columns from a two dimensional array
by sasikumar (Monk) on Dec 21, 2004 at 10:25 UTC
    Hi

    I was able to get the whole column value from the single row but still for a single column it seems to do wonders. Any idea reagarding it.

    my @array=( [0,1,2,3,4], [25,26,27,28,29], [10,11,12,13,14] ); print @{@array[0]}[0..4];

    This prints 01234 as required.

    But
    my @array=( [0,1,2,3,4], [25,26,27,28,29], [10,11,12,13,14] ); print @{@array[0..2]}[3]

    This prints 13... The logic is ok but the way arrays are constructed in perl marks this limitation.

    Update: I hope this is a limitation in perl. The basic datastructure that is used to store the arrays store the start locatation of the array. In case of multidimentional arrays its the array of arrays so it is stored as a reference to the reference where it points out to the row. Hence we can retrive the row in above mentioned method but not the vice versa.
    So it not possible to get the 0..23 from the array without the use of for loop or any other extra logic
    Thanks
    Sasi Kumar

      It's not a limitation of Perl at all. At least for some values of “limitation”.

      The cause of this is that there is no two dimensional array in Perl. You are constructing a representation of such by using an array of arrays, but a one-dimensional array of one-dimensional arrays is not the same as a two dimensional array. There are fewer constraints on such a data structure (the subarrays need not be uniform in length) but in turn, it also loses some expressiveness since you only address one array at a time. You can still represent all operations possible on a two-dimesional array using an array of arrays, but it does require looping.

      To do what the OP wants to do, it would also have to be possible to say @{ \@a, \@b, \@c }[ 3 ], which it is not.

      Makeshifts last the longest.

      Here's what's happening; a list in scalar context will return its last element.

      @array[0..2] will return a list, and in @{}'s scalar context, the last element will be returned, which is the last array ref, that's why you get the last array's item 3 only.

      Here's a workaround:

      my @array=( [0,1,2,3,4], [25,26,27,28,29], [10,11,12,13,14] ); print @{@array[$_]}[3] for 0 .. 2;

      He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.
      Chady | http://chady.net/
      Are you a Linux user in Lebanon? join the Lebanese Linux User Group.
      This works nicely in my humble opinion:
      my @array=( [ 0, 1, 2, 3, 4], [25,26,27,28,29], [10,11,12,13,14] ); print @array->[$_]->[3],$/ for (0..3); 3 28 13
      Though it's not exactly what you're asking for.


      ($_='kkvvttuubbooppuuiiffssqqffssmmiibbddllffss')
      =~y~b-v~a-z~s; print