172

ctypeのコレクションがあり、 ( typeTなどの) プロパティがあるp場合、 map-by-extracting-keyを実行する最良の方法は何ですか?TP

val c: Collection[T]
val m: Map[P, T]

1 つの方法は次のとおりです。

m = new HashMap[P, T]
c foreach { t => m add (t.getP, t) }

しかし今、可変マップが必要です。これを1行にして、不変のマップになるようにするためのより良い方法はありますか? (明らかに、Java の場合と同様に、上記を単純なライブラリ ユーティリティに変えることもできますが、Scala では必要ないと思います)

4

13 に答える 13

248

使用できます

c map (t => t.getP -> t) toMap

ただし、これには 2 回のトラバーサルが必要であることに注意してください。

于 2010-07-14T18:56:24.533 に答える
22

可変数のタプルで 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)
于 2009-03-23T21:11:37.087 に答える
20

@James Iry のソリューションに加えて、フォールドを使用してこれを達成することもできます。このソリューションは、タプル メソッドよりもわずかに高速であると思われます (作成されるガベージ オブジェクトが少なくなります)。

val list = List("this", "maps", "string", "to", "length")
val map = list.foldLeft(Map[String, Int]()) { (m, s) => m(s) = s.length }
于 2009-03-24T18:39:42.063 に答える
9

別の解決策 (すべてのタイプで機能するとは限りません)

import scala.collection.breakOut
val m:Map[P, T] = c.map(t => (t.getP, t))(breakOut)

これにより、中間リストの作成が回避されます。詳細はこちら: Scala 2.8 breakOut

于 2013-10-28T19:24:04.243 に答える
8

あなたが達成しようとしていることは少し未定義です。
の 2 つ以上のアイテムがc同じものを共有している場合はどうなり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 }
于 2015-12-03T15:58:06.873 に答える
3
c map (_.getP) zip c

うまく機能し、非常に直感的です

于 2014-12-04T10:37:56.940 に答える
2

これはおそらく、リストをマップに変換する最も効率的な方法ではありませんが、呼び出しコードが読みやすくなります。暗黙的な変換を使用して、mapByメソッドを Listに追加しました。

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 をインポートする必要があることに注意してください。

于 2014-07-27T06:22:49.880 に答える
1

それが価値があることについて、ここにそれをする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))
于 2012-02-04T10:07:23.747 に答える