http://www.perlmonks.org?node_id=1000346

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

Hi guys, I have an issue that needs to be solved as part of my perl code and I feel i am in over my head. Any help would be greatly appreciated. I have an array of hashes and I need to compare the values in these arrays and based on several criteria only keep 1 of the arrays.

Here is an example of whats in my array:

#/usr/bin/perl use warnings; use strict; use Data::Dumper; my $a="A,Star_1GB,MONTH,1000,0"; my $b="B,Unlim60,MONTH,1000,6000"; my $c="C,Unlim,DAY,50,6000"; my $d="D,,MONTH,500,8000"; my @DA_head=qw(name check duration quantity price); my @DA; foreach ($a,$b,$c,$d){ my %rec; @rec{@DA_head} =split(',',$_); push @DA, \%rec; } print Dumper(\@DA);

I need to compare the values in the array, maybe sort them on the following criteria, Here is my criteria:

1. Is there a value in check.

2. what value has the longest duration, this is in field duration and I need to keep MONTH over WEEK over DAY.

3. Which has the highest value in quantity.

4. which has the highest price.

I need then remove all the values to just be left with the array that best matches the criteria.In this case I should only be left with B.

Thanks in Advance.

Rizz.

Replies are listed 'Best First'.
Re: comparing and removing values from an array of hashes
by roboticus (Chancellor) on Oct 22, 2012 at 14:15 UTC

    rizzler:

    I'd suggest adding another column to your data, and taking two passes through the data:

    On the first pass, you can add the new column. Add 0 if there's no value in check, 1 otherwise. Also in this pass, keep track of the maximum quantity and maximum price.

    On the second pass, you can use your maxima that you found in pass 1 and selectively turn the new column to 0 if you want to discard the row.

    Finally, take a pass through the data and copy the rows that still have a 1 in the new column to your results array.

    Strictly, you don't need to add another column for your task, as you could easily perform the missing value test on pass 2. I thought I'd suggest adding it in case you come up with new criteria that may require yet another pass over the data. It's just a simple sifting technique where you keep marking rows to discard. Once you're done, whatever has a 1 in the new column passed all your tests.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      Hi rizzler,

      I think this may help you,

      my %quantity=(YEAR => 5, MONTH => 4, WEEK => 3, DAY => 2, HOUR =>1); my $a1="A,Star_1GB,MONTH,1000,0"; my $b1="B,Unlim60,MONTH,1000,6000"; my $b2="B,Unlim60,YEAR,1000,6000"; my $c1="C,Unlim,DAY,50,6000"; my $c2="C,Unlim,HOUR,50,6000"; my $d1="D,,MONTH,500,8000"; my @vals = ($a1, $b1, $b2, $c1, $c2, $d1); @vals = map{$_->[0]} sort{$quantity{$b->[1]} <=> $quantity{$a->[1]}} s +ort{$b->[2] <=> $a->[2]} sort{$b->[3] <=> $a->[3]} map{[$_, /(YEAR|MO +NTH|WEEK|DAY|HOUR),([^,]*),([^,]*)$/i]} @vals; print join("\n",@vals);

      Output will be:

      B,Unlim60,YEAR,1000,6000 B,Unlim60,MONTH,1000,6000 A,Star_1GB,MONTH,1000,0 D,,MONTH,500,8000 C,Unlim,DAY,50,6000 C,Unlim,HOUR,50,6000

        Thanks :) this does almost everthing I needed. Even with my poor skills I can probably work out the rest.

        Cheers,

        Rizz

Re: comparing and removing values from an array of hashes
by choroba (Cardinal) on Oct 22, 2012 at 14:30 UTC
    For numeric maximum, you can use max from List::Util. To filter the array, use grep.
    #!/usr/bin/perl use warnings; use strict; use Data::Dumper; use List::Util qw(max); my @DA_head = qw(name check duration quantity price); my @DA; foreach ("A,Star_1GB,MONTH,1000,0", "B,Unlim60,MONTH,1000,6000", "C,Unlim,DAY,50,6000", "D,,MONTH,500,8000") { my %rec; @rec{@DA_head} = split /,/, $_; push @DA, \%rec; } my @checked = grep length $_->{check}, @DA; my @max_dur; for my $maxdur (qw/MONTH WEEK DAY/) { @max_dur = grep $maxdur eq $_->{duration}, @checked; last if @max_dur; } my @maximal = @max_dur; for my $attr (qw/quantity price/) { my $max = max map $_->{$attr}, @maximal; @maximal = grep $max == $_->{$attr}, @maximal; } print Dumper \@maximal;
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ