How to get the index of the maximum element in an array?

Viewed 126

I want to find the maximum value in an array and print his index.

I wrote this for print the maximum value; it works but I don't know how print the index.

use feature "say";
use List::Util qw(max);
@x=qw(10 -2 -48 -5 7 34 28);
say "valore massimo: ".max @x;
6 Answers

The core List::Util comes with an almighty reduce, which can be used to direclty work out all kinds of results

use warnings;
use strict;
use feature 'say';

use List::Util qw(reduce);

my @x = qw(10 -2 -48 -5 7 34 28);

my $max_idx = reduce { $x[$a] > $x[$b] ? $a : $b } 0..$#x;

say "Maximal value of $x[$max_idx] is at index $max_idx";

Can wrap this in a simple function, to have a clear name for the operation (max_idx or something, and which can return both the element and its index, perhaps only in list context). Libraries for list utilities often merely package reduce expressions into handy functions.

A utility with the above functionality is max_by from List::UtilsBy, as shown in Silvio Mayolo's answer (but we don't have to make an array of indices first).


A nitpick but I 'd like to mention. The list of (obviously) numbers given in the question as qw(10 -2 -48 -5 7 34 28) is a list of strings ("words"), as qw operator builds things.

These are treated as numbers once they are used that way, as normally done by the interpreter, and all is well. However, since they are clearly to be numbers I prefer to introduce them as such

my @x = (10, -2, -48, -5, 7, 34, 28);

A touch more typing but I find that it conveys the intent more clearly. Again, by all means this is of no consequence for most (any?) code.

List::UtilsBy provides the max_by, for getting a maximum according to some other criterion (which could be another list).

use 5.010;
use List::UtilsBy qw/max_by/;

my @x = qw(10 -2 -48 -5 7 34 28);
my @indices = (0..@x-1);
say max_by { $x[$_] } @indices;

Generally, if you're doing nontrivial list manipulation in Perl, I recommend installing List::AllUtils, which is an all-in-one package including List::Util, List::SomeUtils, and List::UtilsBy.

The task you are doing is absolutely basic and crucial to programming. If you start to learn programming, you should be able to come up with a solution on your own.

Yes, there exists nice modules that make this task a lot more elegant, but if you are learning to programming, you should come up at least with a solution like this, ON YOUR OWN!

printf "%d\n", max_index(10,3,22,5,4,11,33); # prints 6
printf "%s\n", max_index(34,21,100,12,9);    # prints 2

sub max_index {
    my ( @list ) = @_;
    
    my $max_index = 0;
    my $max_value = shift @list;
    
    my $idx = 0;
    for my $current ( @list ) {
        $idx++;
        if ( $current > $max_value ) {
            $max_index = $idx;
            $max_value = $current;
        }
    }
    
    return $max_index;
}

Homework:

  1. What happens if you pass no element to the function? What should be returned?
  2. Make it work with an array reference.
  3. Use a classic for-loop for (..., ..., ...) { ... }, and don't use shift.
  4. What happens if you pass strings to it, instead of numbers?
  5. Do you know a solution to problem 4?

In situation if you would prefer to use only Perl due restriction on Perl module installation - to find index of maximum value in an array you could use following algorithm:

  • assume that first element in the array has $max value
  • compare following array elements with $max
  • store index and value for max element if satisfices the condition
use strict;
use warnings;
use feature 'say';

my @arr = qw(10 -2 -48 -5 7 34 28);
my($ind,$max) = find_max(\@arr);

say "arr[$ind] = $max";

sub find_max {
    my $arr = shift;

    my($i,$max)=(0,$arr->[0]);
    
    for( 1..$#{$arr} ) {
        ($i,$max) = ($_,$arr->[$_]) if $max < $arr->[$_];
    }
    
    return ($i,$max);
}

Output

arr[5] = 34

For a small task such as this, you don't really need to use external libraries.

use strict;
use warnings;
use feature 'say';

my @x = (10, -2, -48, -5, 7, 34, 28);
my $max = 0;     # first index is the max
for (0 .. $#x) {
    if ($x[$_] > $x[$max]) {
        $max = $_;
    }
}
say "@x";
say "Max number is $x[$max] with index $max";

Output:

10 -2 -48 -5 7 34 28
Max number is 34 with index 5

Just loop over the indexes, check the values and save the index with the highest number.

You can try the following code

use List::Util qw(max);

my @x = qw(10 -2 -48 -5 7 34 28);
my ($index) = ( grep { $x[$_] eq max(@x) } 0..$#x );
print "max ", max(@x), " index $index\n";

Output

max 34 index 5
Related