Wie kann ich in Scala eine Karte verwenden und einen Index erhalten?

99

Gibt es eine integrierte Liste / Sequenz, die sich wie mapder Index des Elements verhält und diesen auch bereitstellt?

Geo
quelle

Antworten:

148

Ich glaube, Sie suchen nach 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

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

Sie haben auch Variationen wie:

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

oder:

List("Mary", "had", "a", "little", "lamb").zipWithIndex.foreach( (t) => println(t._2+" "+t._1) )
Viktor Klang
quelle
4
Nun, ich wollte die zipWithIndexMethode verwenden, um den Index in eine Schleife / Karte / was auch immer zu bekommen.
Ziggystar
Zum Beispiel im Vergleich zu einer whileSchleife, die wahrscheinlich zu den schnellsten Optionen gehört.
Ziggystar
Ein Array und eine while-Schleife sind wahrscheinlich so schnell wie möglich.
Viktor Klang
2
@ziggystar Wenn Sie Leistung suchen, müssen Sie etwas Unveränderlichkeit abwägen. Schauen Sie in die zipWithIndex-Funktion. Es wird nur eine Variable verwendet, um eine neue Sammlung von Paaren zu erstellen. Sie können dieselbe Methode zum Inkrementieren einer Variable verwenden, ohne die neue Sammlung zu erstellen, wie Sie es in Java tun würden. Aber es ist kein funktionaler Stil. Überlegen Sie, ob Sie es tatsächlich brauchen.
Cristian Vrabie
Es scheint also, dass selbst zipWithIndex selbst kein funktionaler Stil ist. Wie auch immer, ich denke, es sollte erwähnt werden, dass die Verwendung von viewIhnen verhindern kann, dass eine zusätzliche Liste erstellt und durchlaufen wird.
Hermann
55

Verwenden . Karte in. zipWithIndex

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

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

Ergebnis:

List("a(0)", "b(1)", "c(2)")
Paulo Check
quelle
11
Dies ist das erste anständige Beispiel, bei dem ich a mapwie gewünscht verwendet habe, anstatt nur in a zu drucken foreach.
Greg Chabala
10

Die vorgeschlagenen Lösungen leiden unter der Tatsache, dass sie Zwischensammlungen erstellen oder Variablen einführen, die nicht unbedingt erforderlich sind. Letztendlich müssen Sie nur die Anzahl der Schritte einer Iteration verfolgen. Dies kann durch Auswendiglernen erfolgen. Der resultierende Code könnte so aussehen

myIterable map (doIndexed(someFunction))

Die doIndexed-Funktion umschließt die innere Funktion, die sowohl einen Index als auch die Elemente von empfängt myIterable. Dies ist Ihnen möglicherweise aus JavaScript bekannt.

Hier ist ein Weg, um diesen Zweck zu erreichen. Betrachten Sie das folgende Dienstprogramm:

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

Das ist schon alles was du brauchst. Sie können dies beispielsweise wie folgt anwenden:

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

was zu der Liste führt

List(97, 99, 101)

Auf diese Weise können Sie die üblichen Traversable-Funktionen auf Kosten der Umhüllung Ihrer effektiven Funktion verwenden. Der Overhead ist die Erstellung des Memoizing-Objekts und des Zählers darin. Andernfalls ist diese Lösung in Bezug auf Speicher oder Leistung genauso gut (oder schlecht) wie die Verwendung von nicht indizierten map. Genießen!

Matt Brasen
quelle
1
Dies ist eine sehr elegante Lösung für das Problem. +1, wenn keine temporäre Sammlung erstellt wurde. Funktioniert nicht in einer parallelen Sammlung, ist aber dennoch eine sehr schöne Lösung.
fbl
5
Wenn Sie keine temporäre Sammlung erstellen möchten, verwenden Sie einfach coll.view.zipWithIndexanstelle voncoll.zipWithIndex
tsuna
5

Es gibt CountedIteratorin 2.7.x (was Sie von einem normalen Iterator mit .counted erhalten können). Ich glaube, es wurde in 2.8 veraltet (oder einfach entfernt), aber es ist einfach genug, um Ihre eigenen zu würfeln. Sie müssen den Iterator benennen können:

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)
Rex Kerr
quelle
3

Angenommen, Ihre Sammlung verfügt über eine konstante Zugriffszeit, können Sie die Liste der Indizes anstelle der tatsächlichen Sammlung zuordnen:

val ls = List("a","b","c")
0.until(ls.length).map( i => doStuffWithElem(i,ls(i)) )
Cristian Vrabie
quelle
1
Ein eleganterer Weg, dies zu tun, ist einfach: ls.indices.map(i => doStuffWithElem(i, ls(i))
Assil Ksiksi
3
@aksiksi Nun, zu sehen, dass dies indicesimplementiert ist, da0 until length es so ziemlich dasselbe ist: P
Cristian Vrabie
1
Downvote wegen Komplexität - Iteration mit ls (i) dauert O (n ^ 2)
Moshe Bixenshpaner
1
@ MosheBixenshpaner Fair genug. Mein Beispiel Listwar in der Tat schlecht. Ich habe jedoch erwähnt, dass dies geeignet ist, wenn Ihre Sammlung eine konstante Zugriffszeit hat. Hätte wählen sollen Array.
Cristian Vrabie
1

Verwenden Sie .map in .zipWithIndex mit Map-Datenstruktur

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

Ergebnisse

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

Es gibt zwei Möglichkeiten, dies zu tun.

ZipWithIndex: Erstellt automatisch einen Zähler, der mit 0 beginnt.

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

Die Ausgabe beider Codes lautet:

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

Zip : Verwenden Sie die Zip-Methode mit einem Stream, um einen Zähler zu erstellen. Auf diese Weise können Sie den Startwert steuern.

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

Ergebnis:

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

Wenn Sie auch die Kartenwerte durchsuchen müssen (wie ich musste):

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