117 votes

Comment puis-je utiliser map et recevoir également un index en Scala ?

Existe-t-il une liste/séquence intégrée qui se comporte de la manière suivante map et fournit également l'index de l'élément ?

168voto

Viktor Klang Points 14826

Je crois que vous cherchez 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

Vous avez aussi des variantes comme :

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

ou :

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

4 votes

Eh bien, je voulais dire utiliser le zipWithIndex pour obtenir l'index à l'intérieur d'une boucle, d'une carte ou autre.

0 votes

Par exemple, en comparant à un while qui est probablement l'une des options les plus rapides.

0 votes

Un tableau et une boucle while sont probablement aussi rapides qu'il est possible de l'être.

77voto

Paulo Cheque Points 335

Utilisez . carte en . zipWithIndex

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

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

Résultat :

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

13 votes

C'est le premier exemple décent que j'ai vu qui utilisait une map comme demandé, au lieu d'imprimer simplement à l'intérieur d'un foreach .

12voto

Matt Brasen Points 51

Les solutions proposées souffrent du fait qu'elles créent des collections intermédiaires ou introduisent des variables qui ne sont pas strictement nécessaires. En définitive, il suffit de garder la trace du nombre d'étapes d'une itération. Ceci peut être fait en utilisant la mémorisation. Le code résultant pourrait ressembler à

myIterable map (doIndexed(someFunction))

Le site doIndexed -La fonction enveloppe la fonction intérieure qui reçoit à la fois un indice et les éléments de myIterable . Cela vous est peut-être familier avec JavaScript.

Voici un moyen d'atteindre cet objectif. Considérons l'utilitaire suivant :

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

C'est déjà tout ce dont vous avez besoin. Vous pouvez l'appliquer par exemple comme suit :

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

ce qui donne la liste

List(97, 99, 101)

De cette façon, vous pouvez utiliser les fonctions Traversable habituelles au prix de l'enveloppement de votre fonction effective. La surcharge est la création de l'objet de mémorisation et du compteur qu'il contient. Autrement, cette solution est aussi bonne (ou mauvaise) en termes de mémoire ou de performance que l'utilisation de fonctions non indexées. map . Profitez-en !

1 votes

C'est une solution très élégante au problème. +1 pour ne pas créer une collection temporaire. Cela ne fonctionnera pas dans une collection parallèle, mais cela reste une très bonne solution.

6 votes

Si vous ne voulez pas créer une collection temporaire, utilisez simplement coll.view.zipWithIndex au lieu de coll.zipWithIndex

6voto

Cristian Vrabie Points 1250

Ou, en supposant que votre collection ait un temps d'accès constant, vous pouvez mapper la liste des index au lieu de la collection elle-même :

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

3 votes

Une manière plus élégante de faire cela est simplement : ls.indices.map(i => doStuffWithElem(i, ls(i))

3 votes

@aksiksi Bien, en voyant que indices est mis en œuvre comme 0 until length c'est à peu près la même chose :P

1 votes

Downvote à cause de la complexité - itérer avec ls(i) prend O(n^2)

5voto

Rex Kerr Points 94401

Il y a CountedIterator en 2.7.x (que vous pouvez obtenir à partir d'un itérateur normal avec .counted). Je crois qu'il a été déprécié (ou simplement supprimé) dans la version 2.8, mais il est assez facile de créer le vôtre. Vous devez être capable de nommer l'itérateur :

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)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X