Як я можу використовувати карту та отримувати індекс також у Scala?


99

Чи є вбудований список / послідовність, який би поводився так само mapі забезпечує також індекс елемента?

Відповіді:


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

Цикл масиву та часу, ймовірно, настільки ж швидкий, наскільки це можливо.
Віктор Кланг

2
@ziggystar Якщо ви шукаєте продуктивність, вам потрібно відміняти деяку незмінність. Загляньте всередину функції zipWithIndex. Він просто використовує var для створення нової колекції пар. Ви можете використовувати той самий метод збільшення вару, не будуючи нову колекцію, так, як це робиться в Java. Але це не функціональний стиль. Подумайте, чи справді вам це потрібно.
Крістіан Врабе

Тож здається, навіть zipWithIndex сам по собі не є функціональним стилем. У будь-якому разі, я думаю, що слід зазначити, що за допомогою viewвас повинно бути у змозі запобігти створенню та переходу додаткового Списку.
герман

55

Використовуйте. карта в. 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
Це перший гідний приклад, який я бачив, що використовував mapяк вимагається, а не просто друкувати всередині foreach.
Грег Чабала

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)

Таким чином, ви можете використовувати звичайні функції Traverable за рахунок завершення ефективної функції. Накладні - це створення об'єкта, що запам'ятовується, та лічильника в ньому. В іншому випадку це рішення настільки ж хороше (або погано) з точки зору пам’яті чи продуктивності, як і використання нерозробленого map. Насолоджуйтесь!


1
Це дуже елегантне рішення проблеми. +1 за створення тимчасової колекції. Не буде працювати в паралельній колекції, але все-таки дуже приємне рішення.
fbl

5
Якщо ви не хочете створювати тимчасову колекцію, просто використовуйте coll.view.zipWithIndexзамістьcoll.zipWithIndex
csuna

5

Є CountedIteratorв 2.7.x (який ви можете отримати від звичайного ітератора з .count). Я вважаю, що його було знято (або просто вилучено) в 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

3
@aksiksi Ну, бачачи, що indicesце реалізовано, оскільки0 until length це майже те саме: P
Крістіан Врабі

1
downvote через складність - ітерація з ls (i) займає O (n ^ 2)
Moshe Bixenshpaner

1
@MosheBixenshpaner Ярмарок досить. Мій приклад Listсправді був поганим. Однак я згадував, що це підходить, якщо у вашій колекції є постійний час доступу. Повинні були вибрати Array.
Крістіан Врабі

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

0

Якщо вам також потрібен пошук значень карти (як мені довелося):

val ls = List("a","b","c")
val ls_index_map = ls.zipWithIndex.toMap 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.