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

Hello Monks, further on to multiplication a column of data in csv file by a factor I am trying now to add an index column to that data (sole purpose to know the index when sorting the multi_array to find its maximum value). But I cannot figure out how to increment the counter when adding the third data column. $i++ fails.

$_->[2] = $i++ for @multi_array;

Appreciate your advice!

Replies are listed 'Best First'.
Re: adding an index column to a csv data file
by swl (Prior) on Jun 02, 2019 at 12:17 UTC

    It works for me.

    my @arr = ([1,1], [2,2], [3,3]); # [[1, 1], [2, 2], [3, 3]] $_->[2] = $i++ for @arr; # [[1, 1, 0], [2, 2, 1], [3, 3, 2]]

    Are you sure your @multi_array contains array refs? If so, is the last index 1, such that assigning to index 2 will add a new element? The data in your previous post has a length of 3 elements, so index 2 is already populated.

    You could generalise to:

    $_->[$#{$_}+1] = $i++ for @arr; # or push @{$_}, $i++ for @arr;
      my $i=0; $_->[2] = $i++ for @multi_array;

      works for me too now after resetting $i which masked it.

      You are right, the present data set has two columns and the other one three. I should have mentioned that.

      Thanks for helping out. The generalisation is useful to have.

        You could generalize this a bit further and eliminate the  $i variable (one less thing to worry about):

        c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "my @multi_array = ([ 99, 88 ], [ 77, 66 ], [ 55, 44 ], ); ;; push @{ $multi_array[$_] }, $_ for 0 .. $#multi_array; dd \@multi_array; " [[99, 88, 0], [77, 66, 1], [55, 44, 2]]
        but I'm not sure this is really an improvement WRT clarity and maintainability! unshift or splice could be used to stuff the new element at other offsets in the array. The "pure index" version is even more obscure:
        c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "my @multi_array = ([ 99, 88 ], [ 77, 66 ], [ 55, 44 ], ); ;; $multi_array[$_][ @{$multi_array[$_]} ] = $_ for 0 .. $#multi_array; dd \@multi_array; " [[99, 88, 0], [77, 66, 1], [55, 44, 2]]


        Give a man a fish:  <%-{-{-{-<

      I realized I forgot all about the <cr>. I solved it this way - just for the records.

      my $i = 0; (chomp $_->[1], $_->[2] = $i++) for @multi_array;

      However, printing looks slighty odd due to a missing blank.

      print join(",", @{$_}),"\n" for (@multi_array); 4.20064619e-03, 5.30384531e+04,91 4.24680719e-03, 5.28570000e+04,92 ...
      I wonder why.

        It is probably due to the split not cleaning up leading spaces, or possible sequential spaces. It is hard to say without seeing that part of the code and the data.

        Could you provide the code and some example data? Your other response indicates it is not the same as your previous post.

        In any case, it could be cleaned up using a map or another loop, possibly embedded in your other code (but that could become a bit too obfuscatory).

        @arr = map {s/^\s+//} @arr;
      I suggest the below code as the consolidated answer to my question, if there are no objections. The handling of blanks is redundant but could be modified for other purposes.
      #!/usr/bin/perl -w my @AoA = (); # ArrayOfArrays # read data plus add index while (<DATA>){ s/\s*//g; my @tmp = split ',',$_; $tmp[0] *= 1.0; $tmp[1] /= 1.0; $tmp[2] = $.; push @AoA,\@tmp; } # or #push @AoA, [split(/,\s*/, $_)] for <DATA>; #push @{ $AoA[$_] }, $_ for 0 .. $#AoA; # modify columns foreach my $row (@AoA) { foreach my $item (@$row) { $item =~ s/^\s+//; } $row->[0] *= 1.0; $row->[1] /= 1.0; } # or #($_->[0] *= 1.0, $_->[1] /= 1.0) for @AoA; # screen output printf "%e,%e,%d\n",@$_ for @AoA; __DATA__ 0.00000000e+00, 2.41644835e+00 1.20048018e-04, 2.38938189e+00 2.40096037e-04, 2.36473989e+00

        No objections from me. There will always be ways of rewriting code, but expressiveness is one of the advantages of Perl.