How can I use map and receive an index as well in Scala?

ScalaFunctional ProgrammingMap

Scala Problem Overview


Is there any List/Sequence built-in that behaves like map and provides the element's index as well?

Scala Solutions


Solution 1 - Scala

I believe you're looking for zipWithIndex?

scala> val ls = List("Mary", "had", "a", "little", "lamb")
scala> ls.zipWithIndex.foreach{ case (e, i) => println(i+" "+e) }
0 Mary
1 had
2 a
3 little
4 lamb

From: http://www.artima.com/forums/flat.jsp?forum=283&thread=243570

You also have variations like:

for((e,i) <- List("Mary", "had", "a", "little", "lamb").zipWithIndex) println(i+" "+e)

or:

List("Mary", "had", "a", "little", "lamb").zipWithIndex.foreach( (t) => println(t._2+" "+t._1) )

Solution 2 - Scala

Use .map in .zipWithIndex

val myList = List("a", "b", "c")

myList.zipWithIndex.map { case (element, index) => 
   println(element, index) 
   s"${element}(${index})"
}

Result:

List("a(0)", "b(1)", "c(2)")

Solution 3 - Scala

The proposed solutions suffer from the fact that they create intermediate collections or introduce variables which are not strictly necessary. For ultimately all you need to do is to keep track of the number of steps of an iteration. This can be done using memoizing. The resulting code might look like

myIterable map (doIndexed(someFunction))

The doIndexed-Function wraps the interior function which receives both an index an the elements of myIterable. This might be familiar to you from JavaScript.

Here is a way to achieve this purpose. Consider the following utility:

object TraversableUtil {
    class IndexMemoizingFunction[A, B](f: (Int, A) => B) extends Function1[A, B] {
        private var index = 0
        override def apply(a: A): B = {
            val ret = f(index, a)
            index += 1
            ret
        }
    }

    def doIndexed[A, B](f: (Int, A) => B): A => B = {
        new IndexMemoizingFunction(f)
    }
}

This is already all you need. You can apply this for instance as follows:

import TraversableUtil._
List('a','b','c').map(doIndexed((i, char) => char + i))

which results in the list

List(97, 99, 101)

This way, you can use the usual Traversable-functions at the expense of wrapping your effective function. The overhead is the creation of the memoizing object and the counter therein. Otherwise this solution is as good (or bad) in terms of memory or performance as using unindexed map. Enjoy!

Solution 4 - Scala

There is CountedIterator in 2.7.x (which you can get from a normal iterator with .counted). I believe it's been deprecated (or simply removed) in 2.8, but it's easy enough to roll your own. You do need to be able to name the iterator:

val ci = List("These","are","words").elements.counted
scala> ci map (i => i+"=#"+ci.count) toList
res0: List[java.lang.String] = List(These=#0,are=#1,words=#2)

Solution 5 - Scala

Or, assuming your collection has constant access time, you could map the list of indexes instead of the actual collection:

val ls = List("a","b","c")
0.until(ls.length).map( i => doStuffWithElem(i,ls(i)) )

Solution 6 - Scala

There are two ways of doing this.

ZipWithIndex: Creates a counter automatically starting with 0.

  // zipWithIndex with a map.
  val days = List("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
  days.zipWithIndex.map {
        case (day, count) => println(s"$count is $day")
  }
  // Or use it simply with a for.
  for ((day, count) <- days.zipWithIndex) {
        println(s"$count is $day")
  }

Output of both code will be:

0 is Sun
1 is Mon
2 is Tue
3 is Wed
4 is Thu
5 is Fri
6 is Sat

Zip: Use zip method with a Stream to create a counter. This gives you a way to control the starting value.

for ((day, count) <- days.zip(Stream from 1)) {
  println(s"$count is $day")
}

Result:

1 is Sun
2 is Mon
3 is Tue
4 is Wed
5 is Thu
6 is Fri
7 is Sat

Solution 7 - Scala

Use .map in .zipWithIndex with Map data structure

val sampleMap = Map("a" -> "hello", "b" -> "world", "c" -> "again")

val result = sampleMap.zipWithIndex.map { case ((key, value), index) => 
    s"Key: $key - Value: $value with Index: $index"
}

Results

 List(
       Key: a - Value: hello with Index: 0, 
       Key: b - Value: world with Index: 1, 
       Key: c - Value: again with Index: 2
     )

Solution 8 - Scala

If you require searching the map values as well (like I had to):

val ls = List("a","b","c")
val ls_index_map = ls.zipWithIndex.toMap 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGeoView Question on Stackoverflow
Solution 1 - ScalaViktor KlangView Answer on Stackoverflow
Solution 2 - ScalaPaulo ChequeView Answer on Stackoverflow
Solution 3 - ScalaMatt BrasenView Answer on Stackoverflow
Solution 4 - ScalaRex KerrView Answer on Stackoverflow
Solution 5 - ScalaCristian VrabieView Answer on Stackoverflow
Solution 6 - ScalaMadstuffsView Answer on Stackoverflow
Solution 7 - Scalafgfernandez0321View Answer on Stackoverflow
Solution 8 - ScalaYair BeerView Answer on Stackoverflow