【问题标题】:How do I remove an element from a list by value?如何按值从列表中删除元素?
【发布时间】:2020-03-14 14:15:57
【问题描述】:

我目前正在研究一个以 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]] 返回。

【问题讨论】:

    标签: scala list dictionary filter scala-collections


    【解决方案1】:

    你可以使用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))
    }
    

    【讨论】:

      【解决方案2】:

      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
        }
      

      【讨论】:

        【解决方案3】:

        这应该适合你:

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

        【讨论】:

        • 哦,是的,实际上我以为他忘记在 ans 中添加 List(x,y) 所以我没有排除@jwvh
        • 我不完全确定这些算法是如何工作的,但它们并没有产生预期的结果。在我实际尝试运行的测试中,有超过 600 个用户 ID 和超过 150,000 个电影 ID。当调用'movies(m, "912")' 时,结果的长度应该是 80,但我得到的长度是 1。
        • @k1888914 任何其他答案对你来说都很好,如果现在可能是你的功能错误。
        • 我的意思是,我上面指定的示例只是我要实现的目标的一个示例,但规模要大得多。实际测试数据的规模要大得多。
        • 其中一两个确实适用于指定的示例,但是当使用实际数据运行它们时,会产生错误的结果。
        猜你喜欢
        • 2015-08-14
        • 1970-01-01
        • 1970-01-01
        • 2010-10-12
        • 2020-09-27
        • 1970-01-01
        • 2015-09-13
        相关资源
        最近更新 更多