23

For a Scala List[Int] I can call the method max to find the maximum element value.

How can I find the index of the maximum element?

This is what I am doing now:

val max = list.max 
val index = list.indexOf(max)
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
Phil
  • 46,436
  • 33
  • 110
  • 175
  • sounds like a strange use case. maybe use need a sorted data structure? – andyczerwonka Dec 23 '12 at 14:27
  • Yes you have a point about strange use case, you could say this is "code smell" as the max could have been found when generating the list in the first place. Not enough space to go into why it seems wrong here in this small space, maybe I will update the answer later. – Phil Dec 23 '12 at 15:09

6 Answers6

44

One way to do this is to zip the list with its indices, find the resulting pair with the largest first element, and return the second element of that pair:

scala> List(0, 43, 1, 34, 10).zipWithIndex.maxBy(_._1)._2
res0: Int = 1

This isn't the most efficient way to solve the problem, but it's idiomatic and clear.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • 1
    damn, exactly what I was about to say... i added a case `x.zipWithIndex.maxBy{ case (i,v) => v }._2` –  Dec 23 '12 at 20:18
16

Since Seq is a function in Scala, the following code works:

list.indices.maxBy(list)
Yuichiroh
  • 181
  • 1
  • 5
3

even easier to read would be:

   val g = List(0, 43, 1, 34, 10)
   val g_index=g.indexOf(g.max)
xhudik
  • 2,414
  • 1
  • 21
  • 39
3
  def maxIndex[ T <% Ordered[T] ] (list : List[T]) : Option[Int] = list match {
    case Nil => None
    case head::tail => Some(
        tail.foldLeft((0, head, 1)){
            case ((indexOfMaximum, maximum, index), elem) =>
              if(elem > maximum) (index, elem, index + 1)
              else (indexOfMaximum, maximum, index + 1)
        }._1
    )
  }   //> maxIndex: [T](list: List[T])(implicit evidence$2: T => Ordered[T])Option[Int]


    maxIndex(Nil)                            //> res0: Option[Int] = None
    maxIndex(List(1,2,3,4,3))                //> res1: Option[Int] = Some(3)
    maxIndex(List("a","x","c","d","e"))      //> res2: Option[Int] = Some(1)

    maxIndex(Nil).getOrElse(-1)              //> res3: Int = -1
    maxIndex(List(1,2,3,4,3)).getOrElse(-1)  //> res4: Int = 3
    maxIndex(List(1,2,2,1)).getOrElse(-1)    //> res5: Int = 1

In case there are multiple maximums, it returns the first one's index.

Pros:You can use this with multiple types, it goes through the list only once, you can supply a default index instead of getting exception for empty lists.

Cons:Maybe you prefer exceptions :) Not a one-liner.

Keyel
  • 144
  • 4
  • Hi, where can I read about the meaning of <% and <: operators, it's quite hard to search on Google (or anywhere) for operators that are not words. Also, I think maybe you could use tailrec in your answer ([example](https://gist.github.com/trylks/6164315)). Thank you. – Trylks Aug 06 '13 at 13:12
  • [Found!](http://ofps.oreilly.com/titles/9780596155957/ScalasTypeSystem.html) Sorry for asking. – Trylks Aug 06 '13 at 13:30
  • `maxIndex(List(1,2,3,4,5))` produces index = 3 - This is because, quite literally, you are missing a "head case" and 5 is ending up unchecked. – codeaperature Mar 25 '21 at 05:09
  • Hi! Thank You for alerting me about the bad code! There was an error in the foldLeft argument. I've just edited it from (0, head, 0) to (0, head, 1). Sry, my mistake. The head case is covered by head::tail, tail can be Nil, in this case foldLeft will return (0, head,1), and maxIndex will return 0 as expected. – Keyel Jun 03 '21 at 10:26
1

I think most of the solutions presented here go thru the list twice (or average 1.5 times) -- Once for max and the other for the max position. Perhaps a lot of focus is on what looks pretty?

In order to go thru a non empty list just once, the following can be tried:

list.foldLeft((0, Int.MinValue, -1)) {
    case ((i, max, maxloc), v) => 
        if (v > max) (i + 1, v, i)
        else (i + 1, max, maxloc)}._3
codeaperature
  • 1,089
  • 2
  • 10
  • 25
0

Pimp my library! :)

class AwesomeList(list: List[Int]) {
  def getMaxIndex: Int = {
    val max = list.max
    list.indexOf(max)
  }
}

implicit def makeAwesomeList(xs: List[Int]) = new AwesomeList(xs)
                                              //> makeAwesomeList: (xs: List[Int])scalaconsole.scratchie1.AwesomeList

//Now we can do this:
List(4,2,7,1,5,6) getMaxIndex             //> res0: Int = 2

//And also this:
val myList = List(4,2,7,1,5,6)            //> myList  : List[Int] = List(4, 2, 7, 1, 5, 6)
myList getMaxIndex                        //> res1: Int = 2

//Regular list methods also work
myList filter (_%2==0)                    //> res2: List[Int] = List(4, 2, 6)

More details about this pattern here: http://www.artima.com/weblogs/viewpost.jsp?thread=179766

Plasty Grove
  • 2,807
  • 5
  • 31
  • 42
  • While it easy to write, isn't it go through the list two times? – om-nom-nom Dec 23 '12 at 15:28
  • Yes you're right. I have a way of getting the max in one iteration through the list, but I think readability takes a big hit with that. Also, it involves using myList(index) to fetch the particular item and I'm not sure how efficient that is – Plasty Grove Dec 23 '12 at 16:08