如果我有一个类型为c
的集合T
,并且p
上有一个属性T
(类型为P
,请说),那么做 map-by-extraction-key 的最好方法是什么?
val c: Collection[T]
val m: Map[P, T]
以下是一种方法:
m = new HashMap[P, T]
c foreach { t => m add (t.getP, t) }
但现在我需要一个可变地图。有没有更好的方法来做到这一点,以便它在一行,我最终得到一个不可变的地图? (显然我可以把上面变成一个简单的库实用程序,就像我在Java中那样,但我怀疑在Scala中没有必要)
答案 0 :(得分:211)
您可以使用
c map (t => t.getP -> t) toMap
但请注意,这需要2次遍历。
答案 1 :(得分:18)
您可以使用可变数量的元组构造Map。因此,使用集合上的map方法将其转换为元组集合,然后使用:_ * trick将结果转换为变量参数。
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)
答案 2 :(得分:14)
除了@James Iry的解决方案之外,还可以使用折叠来实现此目的。我怀疑这个解决方案比tuple方法稍快(创建的垃圾对象更少):
val list = List("this", "maps", "string", "to", "length")
val map = list.foldLeft(Map[String, Int]()) { (m, s) => m(s) = s.length }
答案 3 :(得分:9)
这可以通过以下方式折叠整个集合而不变地实现并进行单次遍历。
val map = c.foldLeft(Map[P, T]()) { (m, t) => m + (t.getP -> t) }
解决方案有效,因为添加到不可变Map会返回带有附加条目的新不可变Map,并且此值通过折叠操作用作累加器。
这里的权衡是代码的简单性与效率的关系。因此,对于大型集合,此方法可能比使用2个遍历实现更合适,例如应用map
和toMap
。
答案 4 :(得分:8)
另一种解决方案(可能不适用于所有类型)
import scala.collection.breakOut
val m:Map[P, T] = c.map(t => (t.getP, t))(breakOut)
这可以避免创建中间列表,更多信息请点击此处: Scala 2.8 breakOut
答案 5 :(得分:6)
你想要达到的目标有点不确定
如果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 }
答案 6 :(得分:2)
c map (_.getP) zip c
运作良好,非常直观
答案 7 :(得分:1)
对于它的价值,这里有两种毫无意义的方式:
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))
答案 8 :(得分:1)
这可能不是将列表转换为映射的最有效方法,但它使调用代码更具可读性。我使用隐式转换将 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。
答案 9 :(得分:1)
如何使用zip和toMap?
myList.zip(myList.map(_.length)).toMap
答案 10 :(得分:1)
Scala 2.13 +
代替“突破”
c.map(t => (t.getP, t)).to(Mat)
滚动到“查看”:https://www.scala-lang.org/blog/2017/02/28/collections-rework.html
答案 11 :(得分:-1)
这对我有用:
val personsMap = persons.foldLeft(scala.collection.mutable.Map[Int, PersonDTO]()) {
(m, p) => m(p.id) = p; m
}
地图必须是可变的,并且必须返回地图,因为添加到可变地图不会返回地图。
答案 12 :(得分:-3)
在集合上使用map(),然后使用toMap
val map = list.map(e => (e, e.length)).toMap