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、おそらく最速のオプションの1つであるループと比較します。
ziggystar 2012年

Arrayとwhileループは、おそらくそれが可能な限り高速です。
Viktor Klang

2
@ziggystarパフォーマンスを求める場合は、不変性をトレードオフする必要があります。zipWithIndex関数の内部を調べます。varを使用して新しいペアのコレクションを作成するだけです。Javaの場合と同じように、新しいコレクションを作成せずに、varをインクリメントする同じ方法を使用できます。しかし、それは機能的なスタイルではありません。実際に必要かどうかを考えてください。
クリスティアン・ヴラビー

したがって、zipWithIndex自体も機能的なスタイルではないようです。とにかく、viewあなたがあなたを使用することは、余分なリストの作成とトラバースを防ぐことができるはずであることは言及されるべきだと思います。
ハーマン2013

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
Greg Chabala

10

提案されたソリューションは、それらが中間コレクションを作成するか、厳密に必要ではない変数を導入するという事実に悩まされています。最終的に必要なのは、反復のステップ数を追跡することだけです。これはメモ化を使用して行うことができます。結果のコードは次のようになります

myIterable map (doIndexed(someFunction))

doIndexed-functionインデックスANの要素の両方を受信する内部機能をラップします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関数を使用できます。オーバーヘッドは、メモ化オブジェクトとその中のカウンターの作成です。それ以外の場合、このソリューションは、メモリまたはパフォーマンスの点で、unindexedを使用するのと同じくらい(または悪い)mapです。楽しい!


1
これは問題に対する非常にエレガントな解決策です。一時的なコレクションを作成しない場合は+1。並列コレクションでは機能しませんが、それでも非常に優れたソリューションです。
fbl 2012

5
一時的なコレクションを作成したくない場合は、coll.view.zipWithIndex代わりに次を使用してくださいcoll.zipWithIndex
tsuna

5

ありCountedIterator(.countedであなたが通常のイテレータから取得することができます)2.7.35で。私はそれが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まあ、それindices0 until lengthほとんど同じものとして実装されているのを見て:P
Cristian Vrabie

1
複雑さのために反対票を投じる-ls(i)での反復にはO(n ^ 2)が必要
Moshe Bixenshpaner 2017

1
@MosheBixenshpaner結構です。私の例Listは確かに貧弱でした。ただし、これは、コレクションのアクセス時間が一定である場合に適していることを述べました。選ぶべきだったArray
クリスティアン・ヴラビー2017

1

Mapデータ構造で.zipWithIndexの.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"
}

結果

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

1

これを行うには2つの方法があります。

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メソッドを使用して、カウンターを作成します。これにより、開始値を制御する方法が提供されます。

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 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.