Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

comparing and removing values from an array of hashes

by rizzler (Novice)
on Oct 22, 2012 at 13:53 UTC ( #1000346=perlquestion: print w/replies, xml ) Need Help??
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.


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


    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.


    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.



Re: comparing and removing values from an array of hashes
by choroba (Chancellor) 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;
    لսႽ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1000346]
Approved by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (6)
As of 2017-01-19 21:27 GMT
Find Nodes?
    Voting Booth?
    Do you watch meteor showers?

    Results (172 votes). Check out past polls.