タイプc
のコレクションT
があり、p
にプロパティT
がある場合(タイプP
の例)、最善の方法は何ですかmap-by-extracting-key?を実行するには
val c: Collection[T]
val m: Map[P, T]
1つの方法は次のとおりです。
m = new HashMap[P, T]
c foreach { t => m add (t.getP, t) }
しかし、今ではmutableマップが必要です。これを行うより良い方法があり、1行になり、immutable Mapになりますか? (明らかに、Javaの場合と同様に、上記を単純なライブラリユーティリティに変えることができますが、Scalaには必要がないと思われます)
使用できます
c map (t => t.getP -> t) toMap
ただし、これには2回のトラバースが必要であることに注意してください。
可変数のタプルを使用してMapを構築できます。したがって、コレクションでmapメソッドを使用してそれをタプルのコレクションに変換し、次に:_ *トリックを使用して結果を変数引数に変換します。
scala> val list = List("this", "maps", "string", "to", "length") map {s => (s, s.length)}
list: List[(Java.lang.String, Int)] = List((this,4), (maps,4), (string,6), (to,2), (length,6))
scala> val list = List("this", "is", "a", "bunch", "of", "strings")
list: List[Java.lang.String] = List(this, is, a, bunch, of, strings)
scala> val string2Length = Map(list map {s => (s, s.length)} : _*)
string2Length: scala.collection.immutable.Map[Java.lang.String,Int] = Map(strings -> 7, of -> 2, bunch -> 5, a -> 1, is -> 2, this -> 4)
@James Iryのソリューションに加えて、フォールドを使用してこれを実現することもできます。このソリューションはTupleメソッドよりもわずかに高速であると思われます(作成されるガベージオブジェクトが少なくなります)。
val list = List("this", "maps", "string", "to", "length")
val map = list.foldLeft(Map[String, Int]()) { (m, s) => m(s) = s.length }
これは、次のようにコレクションを折り返すことにより、不変に1回の走査で実装できます。
val map = c.foldLeft(Map[P, T]()) { (m, t) => m + (t.getP -> t) }
不変のマップに追加すると、追加のエントリを含む新しい不変のマップが返され、この値はフォールド操作を通じてアキュムレータとして機能するため、ソリューションが機能します。
ここでのトレードオフは、コードの単純さとその効率です。したがって、大規模なコレクションの場合、このアプローチは、map
およびtoMap
を適用するなど、2つのトラバーサル実装を使用するよりも適している場合があります。
別の解決策(すべてのタイプで機能しない場合があります)
import scala.collection.breakOut
val m:Map[P, T] = c.map(t => (t.getP, t))(breakOut)
これにより、中間リストの作成が回避されます。詳細はこちら Scala 2.8 breakOut
あなたが達成しようとしていることは少し未定義です。c
の2つ以上のアイテムが同じp
を共有している場合はどうなりますか?マップのそのp
にマッピングされるアイテムはどれですか?
これをより正確に見るには、p
とそれを持つすべてのc
アイテムとの間のマップを生成します。
val m: Map[P, Collection[T]]
これは groupBy で簡単に実現できます。
val m: Map[P, Collection[T]] = c.groupBy(t => t.p)
それでも元のマップが必要な場合は、たとえば、p
を最初のt
にマップできます。
val m: Map[P, T] = c.groupBy(t => t.p) map { case (p, ts) => p -> ts.head }
これはおそらく、リストをマップに変換する最も効率的な方法ではありませんが、呼び出しコードをより読みやすくします。暗黙的な変換を使用して、リストにmapByメソッドを追加しました。
implicit def list2ListWithMapBy[T](list: List[T]): ListWithMapBy[T] = {
new ListWithMapBy(list)
}
class ListWithMapBy[V](list: List[V]){
def mapBy[K](keyFunc: V => K) = {
list.map(a => keyFunc(a) -> a).toMap
}
}
呼び出しコードの例:
val list = List("A", "AA", "AAA")
list.mapBy(_.length) //Map(1 -> A, 2 -> AA, 3 -> AAA)
暗黙的な変換のため、呼び出し元のコードはscalaのimplicitConversionsをインポートする必要があることに注意してください。
c map (_.getP) Zip c
うまく機能し、非常に直感的です
価値のあるものとして、2つの無意味の方法があります。
scala> case class Foo(bar: Int)
defined class Foo
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> val c = Vector(Foo(9), Foo(11))
c: scala.collection.immutable.Vector[Foo] = Vector(Foo(9), Foo(11))
scala> c.map(((_: Foo).bar) &&& identity).toMap
res30: scala.collection.immutable.Map[Int,Foo] = Map(9 -> Foo(9), 11 -> Foo(11))
scala> c.map(((_: Foo).bar) >>= (Pair.apply[Int, Foo] _).curried).toMap
res31: scala.collection.immutable.Map[Int,Foo] = Map(9 -> Foo(9), 11 -> Foo(11))
ZipとtoMapを使用してはどうですか?
myList.Zip(myList.map(_.length)).toMap