Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Returning indices with same values for array within hash.

by pzj20012 (Initiate)
on Nov 10, 2013 at 15:11 UTC ( [id://1061907]=perlquestion: print w/replies, xml ) Need Help??

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

Previously I was using the following:

my @NI = (1 ,1 ,3 ,4); my @NI_index = grep { $NI[$_] == min @NI } 0 .. $#NI; print "@NI_index";
This returns indices: "0 1"

Let's say now I want to put the array NI in a hash instead:

my %matrix = (NI => [1 ,1 ,3 ,4] );
How do you get the same output into @NI_index by modifying this line? Or is there another way to do it?
my @NI_index = grep { $NI[$_] == min @NI } 0 .. $#NI;

Thank you very much!

Replies are listed 'Best First'.
Re: Returning indices with same values for array within hash.
by johngg (Canon) on Nov 10, 2013 at 15:48 UTC

    Not answering your query, but running min every time through the grep is likely to be wasteful if you are dealing with large arrays. It might be better to find the minimum once first.

    $ perl -Mstrict -Mwarnings -MList::Util=min -E ' my @NI = ( 1, 1, 3, 4 ); my @NI_index = do { my $min = min @NI; grep { $NI[ $_ ] == $min } 0 .. $#NI; }; say qq{@NI_index};' 0 1 $

    I hope this is of interest.

    Cheers,

    JohnGG

Re: Returning indices with same values for array within hash.
by Kenosis (Priest) on Nov 10, 2013 at 15:28 UTC

    Deferencing the array reference that's the value associated with the hash key "NI" will get you there:

    use strict; use warnings; use List::Util qw/min/; my %matrix = ( NI => [ 1, 1, 3, 4 ] ); my @NI_index = grep { $matrix{NI}->[$_] == min @{ $matrix{NI} } } 0 .. + $#{ $matrix{NI} }; print "@NI_index";

    Output:

    0 1

    Hope this helps!

      yes, but you don't really need the arrow after the first nesting level! =)

      DB<108> %matrix = (NI => [1 ,1 ,3 ,4] ); => ("NI", [1, 1, 3, 4]) DB<109> $matrix{NI}[3] => 4

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        Good point!

Re: Returning indices with same values for array within hash.
by Athanasius (Archbishop) on Nov 10, 2013 at 15:58 UTC
    Or is there another way to do it?

    More of a variation on a theme (which builds on the previous answers):

    use strict; use warnings; use List::Util qw( min ); use List::MoreUtils qw( indexes ); my %matrix = ( NI => [1, 5, 1, 3, 4] ); my $minimum = min @{ $matrix{NI} }; my @indices = indexes { $_ == $minimum } @{ $matrix{NI} }; print "@indices";

    Output:

    1:57 >perl 774_SoPW.pl 0 2 1:57 >

    See List::MoreUtils.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: Returning indices with same values for array within hash.
by AnomalousMonk (Archbishop) on Nov 10, 2013 at 17:00 UTC

    Another variation, also on the shoulders of others:
    Using an intermediate 'convenience' variable
        my $array_ref = $matrix{NI};
    to hold the reference of interest can greatly simplify/clarify matters. It doesn't buy you much in this particular case, but can be very helpful with complex, deeply nested data structures.

    >perl -wMstrict -le "use List::Util qw(min); use List::MoreUtils qw(indexes); ;; my %matrix = ( NI => [ 1, 5, 1, 3, 4 ] ); ;; my @indices = do { my $array_ref = $matrix{NI}; my $mat_min = min @$array_ref; indexes { $_ == $mat_min } @$array_ref; }; print qq{(@indices)}; " (0 2)

    Update: And, of course, the guts of a  do { ... } block can usually be easily lifted out and plunked down in a subroutine:

    >perl -wMstrict -le "use List::Util qw(min); use List::MoreUtils qw(indexes); ;; my %matrix = ( foo => { bar => [ 'oops', { NI => [ 1, 5, 1, 3, 4 ], }, ], }, ); ;; my @indices = get_indices($matrix{foo}{bar}[1]{NI}); print qq{(@indices)}; ;; sub get_indices { my ($ar) = @_; ;; my $mat_min = min @$ar; return indexes { $_ == $mat_min } @$ar; } " (0 2)
Re: Returning indices with same values for array within hash.
by hdb (Monsignor) on Nov 11, 2013 at 09:30 UTC

    All solutions so far have two passes over the array, but it can be done in one as well. I am using each, so you need one of the newer Perls.

    use strict; use warnings; my %matrix = (NI => [2, 1 ,1 ,3 ,4] ); my @NI_index; my $min; while( my ($i, $v) = each @{$matrix{NI}} ) { if( not defined $min or $v < $min ) { $min = $v; # found new minimum @NI_index = (); # empty list } push @NI_index, $i if $v == $min; } print "@NI_index\n";
      ... each ... newer Perl ...

      Perl version 5.12+, and 5.14+ has the "highly experimental" feature of allowing each to directly take a reference:

      >perl -wMstrict -le "my %matrix = (NI => [2, 1, 1, 3, 4] ); ;; my @NI_index; my $min; while (my ($i, $v) = each $matrix{NI}) { if(not defined $min or $v < $min) { $min = $v; @NI_index = (); } push @NI_index, $i if $v == $min; } print qq{@NI_index}; " 1 2

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1061907]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (3)
As of 2024-04-19 17:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found