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

Hi,

I've a 2D array that I would like to edit, so that the elements are grouped based on the first and second columns. The first column contains the quantity and the last one contains the price. Both quantity and price should be summarized on the grouped elements.

Before editing:

1 PC BOX 20,0 1 PC Spare 32,0 1 PC Spare 5,2 1 PE Ak 100,0

After editing:

1 PC BOX 20,0 2 PC Spare 37,2 1 PE Ak 100,0

Br, klaus

Replies are listed 'Best First'.
Re: Grouping of 2D arrays
by shmem (Chancellor) on Apr 20, 2017 at 12:35 UTC

    See use, strict, warnings.
    See perlsyn, perlre, my, push, grep, printf, sprintf.

    use strict; use warnings; my (%items,@list); while(<DATA>) { my @l = $_ =~/(\d+)\s([\w\s]+?)\s+([\d,]+)/; push @list,$l[1]; $items{$l[1]}->{count} += $1; # bug? $l[2] =~s/,/./; $items{$l[1]}->{sum} += $l[2]; } my %s; for (grep {! $s{$_}++} @list) { $items{$_}->{sum} =~ s/\./,/; printf "%d % -9s %s\n", $items{$_}->{count},$_,$items{$_}->{sum}; } __DATA__ 1 PC BOX 20,0 1 PC Spare 32,0 1 PC Spare 5,2 1 PE Ak 100,0

    This should get you started. Don't use this as is, understand and improve it. Add code to test whether the array @l is really populated (i.e. the match succeeds), for instance.
    Convert your $ to cents and back, because you don't want floating point operations dealing which currency.
    Why the is the "bug" comment in there?

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: Grouping of 2D arrays
by LanX (Saint) on Apr 20, 2017 at 11:40 UTC
    Guessing your question is about the best strategy

    You need to

    • parse the file line by line
    • split lines into a two level hash while adding the values in the 3rd element
    • print the (sorted) result into another file

    I just noticed you have also a count to accumulate so something like

    $product{PC}{Spare} = { count=>2, sum => 37,2}

    Is a good structure.

    So with which part do you have a problem?

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      $product{PC}{Spare} = { count=>2, sum => 37,2}

      Doesn't this give an "Odd number of elements..." warning? Maybe something like
          $product{PC}{Spare} = { count=>2, sum => [37, 2] };


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

        Probably, (for cultural reasons I don't wonder about a comma as fraction separator).

        But as shmem already said, converting to cents is the way to go to avoid rounding errors.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

Re: Grouping of 2D arrays
by kroach (Pilgrim) on Apr 20, 2017 at 09:41 UTC
    Do you want to group an existing perl array or parse a text file? Could you show us your current code?

      Hi,

      I would like to group a existing perl array. The array is created based on a parsed txt file.

      while (my $row2 = <$fh2>) { chomp $row; my @records2 = split(';', $row2); my @items = ($records2[3], $records2[4], $records2[5], $records2[7], $ +records2[8]);

      I know based on the file the quantity of items, so I have a if statement that knows when all the items are in the array. This if statement should also do the grouping and present the information to the user.

      $colli_amount++; if ($total_colli_amount == $colli_amount) {}
      my @array; $array[0] = 1; $array[1] = 'PC BOX'; $array[2] = '20,0'; my @array1; $array1[0] = [ $array[0], $array[1], $array[2] ]; $array[0] = 1; $array[1] = 'PC Spare'; $array[2] = '32,0'; $array1[1] = [ $array[0], $array[1], $array[2] ]; $array[0] = 1; $array[1] = 'PC Spare'; $array[2] = '5,2'; $array1[2] = [ $array[0], $array[1], $array[2] ]; $array[0] = 1; $array[1] = 'PE Ak'; $array[2] = '100,0'; $array1[3] = [ $array[0], $array[1], $array[2] ];
Re: Grouping of 2D arrays
by thanos1983 (Parson) on Apr 20, 2017 at 17:14 UTC

    Hello kfriman,

    Is this something that you are looking for?

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my @AoA = ( [ "1", "PC BOX", "20.0" ], [ "1", "PC Spare", "32.0" ], [ "1", "PC Spare", "2.0" ], [ "1", "PC Spare", "5.2" ], [ "1", "PE Ak", "100.0" ], ); sub process_dublications { my $j = 0; my @AoA_local; my ($hashRefAoH) = @_; # print the whole thing with indices foreach my $key ( keys %{$hashRefAoH} ) { my $total; my $element = 0; foreach my $i ( 0 .. $#{ $hashRefAoH->{$key} } ) { $total += $hashRefAoH->{$key}[$i]; $element++; } push @{$AoA_local[$j++]}, $element, $key, $total; } return \@AoA_local; } my %HoA; # print the whole thing one at a time for my $i ( 0 .. $#AoA ) { next unless push @{ $HoA{$AoA[$i][1]} }, $AoA[$i][2]; } my $AoA_ref = process_dublications(\%HoA); my @sorted = sort { $a->[0] <=> $b->[0] || $a->[2] <=> $b->[2] } @{$Ao +A_ref}; print Dumper \@sorted; __END__ $VAR1 = [ [ 1, 'PC BOX', 20 ], [ 1, 'PE Ak', 100 ], [ 3, 'PC Spare', '39.2' ] ];

    If you really want to keep the comma on the float you need to convert the comma to dot and after the calculation vise versa.

    Hope this helps.

    Seeking for Perl wisdom...on the process of learning...not there...yet!