ฉันจะใช้แผนที่และรับดัชนีใน Scala ได้อย่างไร


คำตอบ:


148

ฉันเชื่อว่าคุณกำลังมองหา 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

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

คุณยังมีรูปแบบต่างๆเช่น:

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

หรือ:

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

4
ฉันหมายถึงใช้zipWithIndexวิธีรับดัชนีภายในลูป / แผนที่ / อะไรก็ได้
ziggystar

ตัวอย่างเช่นการเปรียบเทียบกับwhileลูปซึ่งอาจเป็นตัวเลือกที่เร็วที่สุด
ziggystar

Array และ while loop อาจเร็วที่สุดเท่าที่จะทำได้
วิคเตอร์กลาง

2
@ziggystar หากคุณกำลังมองหาประสิทธิภาพคุณจำเป็นต้องแลกกับความไม่เปลี่ยนรูป ดูภายในฟังก์ชัน zipWithIndex เพียงแค่ใช้ var เพื่อสร้างคอลเลกชันคู่ใหม่ คุณสามารถใช้วิธีการเดียวกันในการเพิ่ม var โดยไม่ต้องสร้างคอลเลกชันใหม่เช่นเดียวกับที่คุณทำใน Java แต่มันไม่ใช่สไตล์การทำงาน คิดว่าถ้าคุณต้องการจริง
Cristian Vrabie

ดูเหมือนว่าแม้แต่ zipWithIndex เองก็ไม่ใช่สไตล์การทำงาน อย่างไรก็ตามฉันคิดว่าควรกล่าวถึงว่าการใช้viewคุณควรจะสามารถป้องกันการสร้างและข้ามผ่านรายการเพิ่มเติมได้
herman

56

ใช้. แผนที่ใน. zipWithIndex

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

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

ผลลัพธ์:

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

11
นี่เป็นตัวอย่างแรกที่ดีที่ฉันเคยเห็นว่าใช้ a mapตามที่ร้องขอแทนที่จะพิมพ์ภายในไฟล์foreach.
Greg Chabala

10

วิธีแก้ปัญหาที่นำเสนอต้องทนทุกข์ทรมานจากการที่พวกเขาสร้างคอลเล็กชันระดับกลางหรือแนะนำตัวแปรที่ไม่จำเป็น ในที่สุดสิ่งที่คุณต้องทำคือติดตามจำนวนขั้นตอนของการทำซ้ำ ซึ่งสามารถทำได้โดยใช้การบันทึก รหัสผลลัพธ์อาจมีลักษณะดังนี้

myIterable map (doIndexed(someFunction))

doIndexedฟังก์ชั่ตัดฟังก์ชั่นการตกแต่งภายในที่ได้รับทั้งดัชนีองค์ประกอบของmyIterableฟังก์ชั่ตัดฟังก์ชั่นการตกแต่งภายในที่ได้รับทั้งดัชนีองค์ประกอบของสิ่งนี้อาจคุ้นเคยกับคุณจาก JavaScript

นี่คือวิธีที่จะบรรลุวัตถุประสงค์นี้ พิจารณายูทิลิตี้ต่อไปนี้:

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

นี่คือสิ่งที่คุณต้องการอยู่แล้ว คุณสามารถนำสิ่งนี้ไปใช้ได้เช่น:

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

ซึ่งผลลัพธ์ในรายการ

List(97, 99, 101)

ด้วยวิธีนี้คุณสามารถใช้ฟังก์ชัน Traversable ตามปกติโดยเสียค่าใช้จ่ายในการรวมฟังก์ชันที่มีประสิทธิภาพของคุณ ค่าใช้จ่ายคือการสร้างวัตถุบันทึกและตัวนับในนั้น มิฉะนั้นการแก้ปัญหานี้เป็นสิ่งที่ดี (หรือไม่ดี) ในแง่ของหน่วยความจำหรือประสิทธิภาพการใช้ mapunindexed สนุก!


1
นี่เป็นวิธีแก้ปัญหาที่สง่างามมาก +1 สำหรับการไม่สร้างคอลเล็กชันชั่วคราว จะไม่ทำงานในคอลเลกชันคู่ขนาน แต่ก็ยังเป็นทางออกที่ดีมาก
fbl

5
หากคุณไม่ต้องการสร้างคอลเล็กชันชั่วคราวให้ใช้coll.view.zipWithIndexแทนcoll.zipWithIndex
tsuna

5

มีอยู่CountedIteratorใน 2.7.x (ซึ่งคุณสามารถหาได้จากตัววนซ้ำปกติที่มี. นับ) ฉันเชื่อว่ามันถูกเลิกใช้งานแล้ว (หรือเพียงแค่ลบออก) ใน 2.8 แต่มันง่ายพอที่จะม้วนของคุณเอง คุณต้องสามารถตั้งชื่อตัววนซ้ำได้:

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

หรือสมมติว่าคอลเล็กชันของคุณมีเวลาในการเข้าถึงคงที่คุณสามารถแมปรายการดัชนีแทนการรวบรวมจริงได้:

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

1
วิธีที่สวยงามกว่านี้คือ: ls.indices.map(i => doStuffWithElem(i, ls(i))
Assil Ksiksi


1
ลงคะแนนเนื่องจากความซับซ้อน - การทำซ้ำด้วย ls (i) ใช้เวลา O (n ^ 2)
Moshe Bixenshpaner

1
@MosheBixenshpaner พอใช้ ตัวอย่างของฉันListช่างน่าสงสารจริงๆ อย่างไรก็ตามฉันได้กล่าวถึงสิ่งนี้ว่าเหมาะสมหากคอลเล็กชันของคุณมีเวลาในการเข้าถึงคงที่ Arrayควรจะมีการเลือก
Cristian Vrabie

1

ใช้. mapใน. zipWithIndexกับโครงสร้างข้อมูลแผนที่

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

ผล

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

1

มีสองวิธีในการทำเช่นนี้

ZipWithIndex:สร้างตัวนับโดยอัตโนมัติโดยเริ่มต้นด้วย 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")
  }

ผลลัพธ์ของรหัสทั้งสองจะเป็น:

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

Zip : ใช้วิธี zip กับ Stream เพื่อสร้างตัวนับ วิธีนี้ช่วยให้คุณสามารถควบคุมค่าเริ่มต้นได้

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

ผลลัพธ์:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.