我目前正在研究一个以Map [String,List [String]]和一个String作为参数的函数。该地图包含用户ID和他们喜欢的电影的ID。我需要做的是,返回一个List [List [String]],其中包含其他喜欢传递给函数的电影的用户所喜欢的电影。
函数声明如下:
def movies(m: Map[String, List[String]], mov: String) : List[List[String]]= {
}
因此,请想象以下内容:
val m1 : [Map[Int, List[String]]] = Map(1 ‐> List("b", "a"), 2 ‐> List("y", "x"), 3 ‐> List("c", "a"))
val movieID = "a"
movies(m1, movieId)
这应该返回:
List(List("b"), List("c"))
我尝试使用
m1.filter(x => x._2.contains(movieID))
因此,只有包含movieID的列表保留在地图中,但是我的问题是我需要从其中出现的每个列表中删除movieID,然后将结果作为List [List [String]]返回。
答案 0 :(得分:2)
您可以使用collect
:
val m = Map("1" -> List("b", "a"), "2" -> List("y", "x"), "3" -> List("c", "a"))
def movies(m: Map[String, List[String]], mov: String) = m.collect {
case (_, l) if l.contains(mov) => l.filterNot(_ == mov)
}
movies(m, "a") //List(List(b), List(c))
此方法的问题是,它将对每个电影列表进行两次迭代,第一次使用contains
,第二次使用filterNot
。我们可以优化它的尾递归函数,该函数将查找元素,如果找到,则返回不包含它的列表:
import scala.annotation.tailrec
def movies(m: Map[String, List[String]], mov: String) = {
@tailrec
def withoutElement[T](l: List[T], mov: T, acc: List[T] = Nil): Option[List[T]] = {
l match {
case x :: xs if x == mov => Some(acc.reverse ++ xs)
case x :: xs => withoutElement(xs, mov, x :: acc)
case Nil => None
}
}
m.values.flatMap(withoutElement(_, mov))
}
答案 1 :(得分:1)
Krzysztof的解决方案很好。这是另一种遍历每个List
一次的方法。
def movies(m: Map[String, List[String]], mov: String) =
m.values.toList.flatMap{ss =>
val tpl = ss.foldLeft((false, List.empty[String])){
case ((_,res), `mov`) => (true, res)
case ((keep,res), str) => (keep, str::res)
}
if (tpl._1) Some(tpl._2) else None
}
答案 2 :(得分:1)
这应该对您有用:
object DemoAbc extends App {
val m1 = Map(1 -> List("b", "a"), 2 -> List("y", "x"), 3 -> List("c", "a"))
val movieID = "a"
def movies(m: Map[Int, List[String]], mov: String): List[List[String]] = {
val ans = m.foldLeft(List.empty[List[String]])((a: List[List[String]], b: (Int, List[String])) => {
if (b._2.contains(mov))
b._2.filter(_ != mov) :: a
else a
})
ans
}
print(movies(m1, movieID))
}