Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

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
[davido]: yeah, umount -f isn't powerful enough.
[tye]: probably something in the init subsystem that does the mounting that you could disable and reboot.
davido needs to close laptop to board flight home from yapc.
[davido]: I'll look into it in a few hours probably.
[davido]: when i do get to that point I think I'll do it in a vm snapshot just in case. :)
[oiskuu]: tye, you were right: loginuid/sessionid are part of task struct if compiled with AUDITSYSCALL. I have some doubts if you should actually depend on that feature.
[Corion]: oiskuu: Depends on what you want to do with that information
[tye]: I'm not depending on that feature. But I could in this environment. I'm using getlogin(). shrug
[Corion]: For benign logging (which user started this DB instance), it's OK
[tye]: We use auditd for security monitoring. So we can rely on auditing being enabled. I'm not sure who would want to not be able to audit. Maybe some VM inside another system with audit?

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (7)
As of 2017-06-23 20:09 GMT
Find Nodes?
    Voting Booth?
    How many monitors do you use while coding?

    Results (555 votes). Check out past polls.