¿Cómo puedo usar el mapa y recibir un índice también en Scala?


99

¿Hay alguna Lista / Secuencia incorporada que se comporte mapy proporcione también el índice del elemento?

Respuestas:


148

Creo que estás buscando 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

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

También tienes variaciones como:

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

o:

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

4
Bueno, me refería a usar el zipWithIndexmétodo para obtener el índice dentro de un bucle / mapa / lo que sea.
ziggystar

Por ejemplo, comparándolo con un whilebucle, que probablemente se encuentre entre las opciones más rápidas.
ziggystar

Una matriz y un bucle while son probablemente lo más rápido posible.
Viktor Klang

2
@ziggystar Si está buscando rendimiento, necesita sacrificar algo de inmutabilidad. Mire dentro de la función zipWithIndex. Solo usa una var para construir una nueva colección de pares. Podría usar el mismo método para incrementar una var sin construir la nueva colección, tal como lo haría en Java. Pero no es un estilo funcional. Piense si realmente lo necesita.
Cristian Vrabie

Así que parece que incluso zipWithIndex en sí mismo no es un estilo funcional. De todos modos, creo que debería mencionarse que el uso de viewusted debería poder evitar crear y atravesar una Lista adicional.
herman

55

Utilizar . mapa en. zipWithIndex

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

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

Resultado:

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

11
Este es el primer ejemplo decente que he visto en el que se utilizó un maparchivo tal como se solicitó en lugar de solo imprimir dentro de un archivo foreach.
Greg Chabala

10

Las soluciones propuestas adolecen del hecho de que crean colecciones intermedias o introducen variables que no son estrictamente necesarias. Porque, en última instancia, todo lo que necesita hacer es realizar un seguimiento del número de pasos de una iteración. Esto se puede hacer mediante la memorización. El código resultante podría verse así

myIterable map (doIndexed(someFunction))

La doIndexed-Función envuelve la función interior que recibe tanto un índice como los elementos de myIterable. Esto puede resultarle familiar gracias a JavaScript.

He aquí una forma de lograr este propósito. Considere la siguiente utilidad:

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)
    }
}

Esto ya es todo lo que necesitas. Puede aplicar esto, por ejemplo, de la siguiente manera:

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

que resulta en la lista

List(97, 99, 101)

De esta manera, puede usar las funciones habituales de Traversable a expensas de envolver su función efectiva. La sobrecarga es la creación del objeto de memorización y el contador que contiene. De lo contrario, esta solución es tan buena (o mala) en términos de memoria o rendimiento como el uso sin indexar map. ¡Disfrutar!


1
Esta es una solución muy elegante al problema. +1 por no crear una colección temporal. No funcionará en una colección paralela, pero sigue siendo una solución muy agradable.
fbl

5
Si no desea crear una colección temporal, use en coll.view.zipWithIndexlugar decoll.zipWithIndex
tsuna

5

Hay CountedIteratoren 2.7.x (que puede obtener de un iterador normal con .counted). Creo que ha quedado obsoleto (o simplemente se ha eliminado) en 2.8, pero es bastante fácil lanzar el suyo. Necesita poder nombrar el iterador:

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)

3

O, asumiendo que su colección tiene un tiempo de acceso constante, podría mapear la lista de índices en lugar de la colección real:

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

1
Una forma más elegante de hacer esto es simplemente: ls.indices.map(i => doStuffWithElem(i, ls(i))
Assil Ksiksi

3
@aksiksi Bueno, al ver que indicesse implementa,0 until length es más o menos lo mismo: P
Cristian Vrabie

1
voto negativo debido a la complejidad: iterar con ls (i) toma O (n ^ 2)
Moshe Bixenshpaner

1
@MosheBixenshpaner Bastante justo. Mi ejemplo de Listfue realmente pobre. Sin embargo, mencioné que esto es adecuado si su colección tiene un tiempo de acceso constante. Debería haber elegido Array.
Cristian Vrabie

1

Utilice .map en .zipWithIndex con estructura de datos de mapa

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"
}

Resultados

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

1

Hay dos formas de hacer esto.

ZipWithIndex: crea un contador que comienza automáticamente con 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")
  }

La salida de ambos códigos será:

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

Zip : use el método zip con un Stream para crear un contador. Esto le brinda una forma de controlar el valor inicial.

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

Resultado:

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

0

Si también necesita buscar los valores del mapa (como tuve que hacerlo):

val ls = List("a","b","c")
val ls_index_map = ls.zipWithIndex.toMap 
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.