Bagaimana saya bisa menggunakan peta dan menerima indeks juga di Scala?

99

Apakah ada List / Sequence built-in yang berperilaku seperti mapdan menyediakan indeks elemen juga?

Geo
sumber

Jawaban:

148

Saya yakin Anda sedang mencari 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

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

Anda juga memiliki variasi seperti:

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

atau:

List("Mary", "had", "a", "little", "lamb").zipWithIndex.foreach( (t) => println(t._2+" "+t._1) )
Viktor Klang
sumber
4
Nah, maksud saya menggunakan zipWithIndexmetode untuk mendapatkan indeks di dalam loop / map / apapun.
ziggystar
Misalnya membandingkan dengan sebuah whileloop, yang mungkin merupakan salah satu opsi tercepat.
ziggystar
Array dan while loop mungkin secepat yang ia bisa.
Viktor Klang
2
@ziggystar Jika Anda mencari performa, Anda perlu menukar beberapa kekekalan. Lihat di dalam fungsi zipWithIndex. Ini hanya menggunakan var untuk membangun koleksi pasangan baru. Anda dapat menggunakan metode yang sama untuk menambahkan var tanpa membuat koleksi baru, seperti yang akan Anda lakukan di Java. Tapi itu bukan gaya fungsional. Pikirkan jika Anda benar-benar membutuhkannya.
Cristian Vrabie
Jadi tampaknya bahkan zipWithIndex itu sendiri bukanlah gaya fungsional. Bagaimanapun saya pikir harus disebutkan bahwa menggunakan viewAnda harus dapat mencegah pembuatan dan melintasi Daftar tambahan.
herman
55

Gunakan. memetakan . zipWithIndex

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

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

Hasil:

List("a(0)", "b(1)", "c(2)")
Paulo Check
sumber
11
Ini adalah contoh layak pertama yang pernah saya lihat yang menggunakan a mapseperti yang diminta alih-alih hanya mencetak di dalam file foreach.
Greg Chabala
10

Solusi yang diusulkan menderita karena mereka membuat koleksi perantara atau memasukkan variabel yang tidak terlalu diperlukan. Karena pada akhirnya yang perlu Anda lakukan adalah melacak jumlah langkah iterasi. Ini dapat dilakukan dengan menggunakan memoizing. Kode yang dihasilkan mungkin terlihat seperti ini

myIterable map (doIndexed(someFunction))

The doIndexed-Fungsi membungkus fungsi interior yang menerima indeks dan elemen myIterable. Ini mungkin tidak asing bagi Anda dari JavaScript.

Inilah cara untuk mencapai tujuan ini. Pertimbangkan utilitas berikut:

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

Ini sudah semua yang Anda butuhkan. Anda dapat menerapkan ini misalnya sebagai berikut:

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

yang menghasilkan daftar

List(97, 99, 101)

Dengan cara ini, Anda dapat menggunakan fungsi Traversable biasa dengan mengorbankan fungsi efektif Anda. Overhead adalah pembuatan objek memo dan penghitung di dalamnya. Jika tidak, solusi ini sama baiknya (atau buruk) dalam hal memori atau kinerja seperti menggunakan tidak terindeks map. Nikmati!

Matt Brasen
sumber
1
Ini adalah solusi yang sangat elegan untuk masalah ini. +1 untuk tidak membuat koleksi sementara. Tidak akan berfungsi dalam koleksi paralel, tetapi masih merupakan solusi yang sangat bagus.
fbl
5
Jika Anda tidak ingin membuat koleksi sementara, hanya menggunakan coll.view.zipWithIndexbukannyacoll.zipWithIndex
Tsuna
5

Ada CountedIteratordi 2.7.x (yang bisa Anda dapatkan dari iterator normal dengan .counted). Saya yakin ini sudah usang (atau hanya dihapus) di 2.8, tetapi cukup mudah untuk menggulirnya sendiri. Anda harus dapat memberi nama 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)
Rex Kerr
sumber
3

Atau, dengan asumsi koleksi Anda memiliki waktu akses konstan, Anda dapat memetakan daftar indeks alih-alih koleksi sebenarnya:

val ls = List("a","b","c")
0.until(ls.length).map( i => doStuffWithElem(i,ls(i)) )
Cristian Vrabie
sumber
1
Cara yang lebih elegan untuk melakukan ini adalah: ls.indices.map(i => doStuffWithElem(i, ls(i))
Assil Ksiksi
3
@aksiksi Nah, melihat yang indicesdiimplementasikan karena0 until length hampir sama: P
Cristian Vrabie
1
tidak suka karena kerumitan - iterasi dengan ls (i) membutuhkan O (n ^ 2)
Moshe Bixenshpaner
1
@Soal_Atas_sby Cukup adil. Teladan saya Listmemang buruk. Namun saya menyebutkan bahwa ini cocok jika koleksi Anda memiliki waktu akses yang konstan. Seharusnya memilih Array.
Cristian Vrabie
1

Gunakan .map di .zipWithIndex dengan struktur data Map

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

Hasil

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

Ada dua cara untuk melakukan hal ini.

ZipWithIndex: Membuat penghitung secara otomatis dimulai dengan 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 dari kedua kode tersebut adalah:

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

Zip : Gunakan metode zip dengan Stream untuk membuat penghitung. Ini memberi Anda cara untuk mengontrol nilai awal.

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

Hasil:

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

Jika Anda perlu mencari nilai peta juga (seperti saya harus):

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