【问题标题】:Filtering a collection based on an arbitrary number of options根据任意数量的选项过滤集合
【发布时间】:2026-01-15 11:00:02
【问题描述】:

如何重构以下 Scala 函数以使用惯用的最佳实践?

  def getFilteredList(ids: Seq[Int], 
      idsMustBeInThisListIfItExists: Option[Seq[Int]], 
      idsMustAlsoBeInThisListIfItExists: Option[Seq[Int]]): Seq[Int] = {

    var output = ids

    if (idsMustBeInThisListIfItExists.isDefined) {
      output = output.intersect(idsMustBeInThisListIfItExists.get)
    }
    if (idsMustAlsoBeInThisListIfItExists.isDefined) {
      output = output.intersect(idsMustAlsoBeInThisListIfItExists.get)
    }

    output 
  }

预期 IO:

val ids = Seq(1,2,3,4,5)
val output1 = getFilteredList(ids, None, Some(Seq(3,5))) // 3, 5
val output2 = getFilteredList(ids, None, None) // 1,2,3,4,5
val output3 = getFilteredList(ids, Some(Seq(1,2)), None) // 1,2
val output4 = getFilteredList(ids, Some(Seq(1)), Some(Seq(5))) // 1,5

感谢您的宝贵时间。

【问题讨论】:

    标签: scala


    【解决方案1】:

    这是一个简单的方法:

      implicit class SeqAugmenter[T](val seq: Seq[T]) extends AnyVal {
        def intersect(opt: Option[Seq[T]]): Seq[T] = {
          opt.fold(seq)(seq intersect _)
        }
      }
    
      def getFilteredList(ids: Seq[Int],
        idsMustBeInThisListIfItExists: Option[Seq[Int]],
        idsMustAlsoBeInThisListIfItExists: Option[Seq[Int]]
      ): Seq[Int] = {
        ids intersect
          idsMustBeInThisListIfItExists intersect 
          idsMustAlsoBeInThisListIfItExists
      }
    

    【讨论】:

    • 感谢您抽出时间用惯用的代码示例做出回应。我的想法是,这个解决方案具有讽刺意味,因为它回退到 Java 遗留的匿名(或“隐式”)类来解决问题。
    • 这与匿名类无关,我认为您误解了这些概念。这只是为了使代码更流畅,仅此而已。 alvinalexander.com/scala/scala-2.10-implicit-class-example
    • 有一个相当简单的控制结构来实现预期的输出; scala 的方式是不惜一切代价避免#isDefined,这就是我问 Q 的原因,具有讽刺意味的是,当避免类是使用 Scala 而不是 Java 的原因时,惯用的解决方案是求助于类。
    • @newToScala 好的,但真正的技巧是implicit 并使用AnyVal 来避免拳击,所以有很多Java 无法做到的事情,类位是绝对是混合的一部分,但最不有趣的一点。这里有一些细节:*.com/questions/14929422/…
    【解决方案2】:

    另一种没有理解和隐含的方式:

    def getFilteredList(ids: Seq[Int],
                      idsMustBeInThisListIfItExists: Option[Seq[Int]],
                      idsMustAlsoBeInThisListIfItExists: Option[Seq[Int]]): Seq[Int] = {
    
      val output1 = ids.intersect(idsMustBeInThisListIfItExists.getOrElse(ids))
      val output2 = output1.intersect(idsMustAlsoBeInThisListIfItExists.getOrElse(output1))
    
      output2
    }
    

    【讨论】:

      【解决方案3】:

      另一种类似的方式,没有隐含。

      def getFilteredList[A](ids: Seq[A],
                             idsMustBeInThisListIfItExists: Option[Seq[A]],
                             idsMustAlsoBeInThisListIfItExists: Option[Seq[A]]): Seq[A] = {
        val  a = intersect(Some(ids), idsMustBeInThisListIfItExists)(ids)
        val  b = intersect(Some(a), idsMustAlsoBeInThisListIfItExists)(a)
        b
      }
      
      def intersect[A](ma: Option[Seq[A]], mb: Option[Seq[A]])(default: Seq[A]) = {
        (for {
           a <- ma
           b <- mb
         } yield {
           a.intersect(b)
         }).getOrElse(default)
      }
      

      【讨论】: