斯卡拉最好的方式把一个集合成一个按地图按键?

如果我有一个typesT的集合c ,并且T有一个属性p (例如,typesP ),那么通过提取键来执行映射的最佳方法是什么?

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

其中一个方法如下:

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

但现在我需要一个可变的地图。 有没有更好的方式做到这一点,所以它在1行,我最终得到一个不可变的地图? (很明显,我可以把上面的代码变成一个简单的库工具,就像我在Java中那样,但是我怀疑在Scala中是没有必要的)

您可以使用

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

但请注意,这需要2次遍历。

你可以用可变数量的元组构造一个Map。 因此,使用集合上的map方法将其转换为元组集合,然后使用:_ *技巧将结果转换为variables参数。

 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的解决scheme之外,还可以使用折叠来实现这一点。 我怀疑这个解决scheme比元组方法稍快(创build的垃圾对象更less):

 val list = List("this", "maps", "string", "to", "length") val map = list.foldLeft(Map[String, Int]()) { (m, s) => m(s) = s.length } 

另一个解决scheme(可能不适用于所有types)

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

这避免了创build中介列表,更多信息在这里: Scala 2.8 breakOut

 c map (_.getP) zip c 

运作良好,非常直观

你试图达到什么是有点不确定。
如果c两个或多个项目共享相同的p怎么办? 哪个项目将被映射到地图上的那个p

更准确的方法是在p和所有c项之间生成一个映射:

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

这可以通过groupBy轻松实现:

 val m: Map[P, Collection[T]] = c.groupBy(t => tp) 

如果你仍然想要原始地图,例如,你可以将p映射到具有它的第一个t

 val m: Map[P, T] = c.groupBy(t => tp) map { case (p, ts) => p -> ts.head } 

这可以通过如下折叠通过该集合而不可变地实现并且通过单个遍历来实现。

 val map = c.foldLeft(Map[P, T]()) { (m, t) => m + (t.getP -> t) } 

该解决scheme的工作原理是,添加到不可变的Map返回一个新的不可变的Map与额外的条目,这个值通过折叠操作作为累加器。

这里的权衡是代码与其效率的简单性。 因此,对于大型集合,这种方法可能比使用2 toMap实现(如应用maptoMap更合适。

为了什么是值得的,这里有两个毫无意义的方法:

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

这可能不是将列表转换为映射的最有效方式,但它使调用代码更具可读性。 我使用隐式转换来将ListBy方法添加到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。

这适用于我:

 val personsMap = persons.foldLeft(scala.collection.mutable.Map[Int, PersonDTO]()) { (m, p) => m(p.id) = p; m } 

Map必须是可变的,Map必须返回,因为添加一个可变Map不会返回一个映射。