【问题标题】:Filter from Seq less/greater elements and only one equal从 Seq 更少/更大的元素中过滤,并且只有一个相等
【发布时间】:2020-09-06 09:18:11
【问题描述】:

我想在 Scala 中实现一种方法,该方法从 Seq 元素中过滤,例如大于提供的值,并额外返回一个相等的元素。例如:

greaterOrEqual(Seq(1,2,3,3,4), 3) shouldBe Seq(3,4)

我最终得到了这样的方法:

def greaterOrEqual(
      seq: ArrayBuffer[Long],
      value: Long
  ): ArrayBuffer[Long] = {
    val greater = seq.filter(_ > value)
    val equal = seq.filter(_ == value)
    if (equal.isEmpty) {
      greater
    } else {
      equal.tail ++ greater
    }
  }

但不知何故,它对我来说看起来不太好:) 此外,我希望有这个方法的通用版本,我不仅可以使用 Long 类型,还可以使用自定义案例类。

你有什么建议吗? 提前致谢。

【问题讨论】:

  • 你可以去掉if,改成equal.take(1) ++ greater。这也适用于空的equal。或者甚至更好,val equal = seq.find(_ == value).toList 然后只是 equal ++ greater

标签: scala scala-collections


【解决方案1】:
def foo[A : Ordering[A]](seq: Seq[A], value: A) = seq.find(_ == value).toList ++ seq.filter(implicitly[Ordering[A]].gt(_,value))

或者(不同的风格)

def foo[A](seq: Seq[A], value: A)(implicit ord: Ordering[A]) = {
   import ord._
   seq.find(_ == value).toList ++ seq.filter(_ > value)
}

以下代码已弃用

罢工>

scala> def foo[A <% Ordered[A]](seq: Seq[A], value: A) = seq.find(_ == value).toList ++  seq.filter(_ > value)
foo: [A](seq: Seq[A], value: A)(implicit evidence$1: A => Ordered[A])List[A]

scala> foo(Seq(1,2,3,3,4,4,5),3)
res8: List[Int] = List(3, 4, 4, 5)

【讨论】:

  • 这似乎是我所需要的。谢谢。我只是想知道我是否想将此函数与 case 类一起使用,它有一个我想比较的数字字段,所以我必须为这个类重载 &gt; 运算符,不是吗?
  • 您似乎需要为您的案例类定义隐式排序,例如 // sort by age implicit object AgeOrdering extends Ordering[Person] { def compare(a:Person, b:Person) = a.age compare b.age } 。见这里stackoverflow.com/questions/22592456/what-are-implicit-objects
  • 不,我的错,你只需要扩展 Ordered case class Bar(i: Int) extends Ordered[Bar] { def compare(that: Bar) = i.compare(that.i) } scala&gt; foo(Seq(Bar(1),Bar(2)),Bar(2)) res12: List[Bar] = List(Bar(2))
  • @LuisMiguelMejíaSuárez 好的,我会删除它。您能展示在这种情况下如何使用 Ordering 类型类方法吗?
【解决方案2】:

这是我的看法(保留原始顺序)。

import scala.collection.mutable.ArrayBuffer

def greaterOrEqual[A]( seq :ArrayBuffer[A], value :A
                     )(implicit ord :Ordering[A]
                      ) : ArrayBuffer[A] =
  seq.foldLeft((ArrayBuffer.empty[A],true)){
    case (acc, x)        if ord.lt(x,value) => acc
    case ((acc,bool), x) if ord.gt(x,value) => (acc :+ x, bool)
    case ((acc,true), x)                    => (acc :+ x, false)
    case (acc, _)                           => acc
  }._1

测试:

greaterOrEqual(ArrayBuffer.from("xawbaxbt"), 'b')
//res0: ArrayBuffer[Char] = ArrayBuffer(x, w, b, x, t)

【讨论】:

    【解决方案3】:

    对于列表上的简单尾递归算法来说,这是一个很好的问题。

    def greaterOrEqual[T : Ordering](elements: List[T])(value: T): List[T] = {
      import Ordering.Implicits._
    
      @annotation.tailrec
      def loop(remaining: List[T], alreadyIncludedEqual: Boolean, acc: List[T]): List[T] =
        remaining match {
          case x :: xs =>
            if (!alreadyIncludedEqual && x == value)
              loop(remaining = xs, alreadyIncludedEqual = true, x :: acc)
            else if (x > value)
              loop(remaining = xs, alreadyIncludedEqual, x :: acc)
            else
              loop(remaining = xs, alreadyIncludedEqual, acc)
    
          case Nil =>
            acc.reverse
        }
    
      loop(remaining = elements, alreadyIncludedEqual = false, acc = List.empty)
    }
    

    你可以这样使用:

    greaterOrEqual(List(1, 3, 2, 3, 4, 0))(3)
    // val res: List[Int] = List(3, 4)
    

    【讨论】:

      【解决方案4】:

      你可以使用下面的sn-p:

      val list = Seq(1,2,3,3,4)
      val value = 3
      
      list.partition(_>=3)._1.toSet.toSeq
      

      这里的分区方法将列表分成两个列表。第一个列表满足给定条件,第二个列表包含其余元素。

      【讨论】:

      • 它也会删除大于value的元素
      • 是的,这就是问题输出的要求。如果您不想删除重复项,则可以跳过执行“toSet”
      • 不,似乎只有等于 value 的元素才需要重复数据删除。至少代码是这样做的
      • equal.tail 可以是一个列表,并且可以有多个相等的值。
      • 你是对的。那我不知道原来的要求是什么
      【解决方案5】:

      对于泛型方法,您可以使用隐式Ordering。任何可以比较元素的类型都可以通过greaterOrEqual方法处理。

      import scala.math.Ordering._
      
      def greaterOrEqual[T](seq: Seq[T], value: T)(implicit ordering: Ordering[T]): Seq[T] = {
        @scala.annotation.tailrec
        def go(xs: List[T], value: T, acc: List[T]): List[T] = {
          xs match {
            case Nil => acc
            case head :: rest if ordering.compare(head, value) == 0 => rest.foldLeft(head :: acc){
              case (result, x) if ordering.compare(x, value) > 0 => x :: result
              case (result, _) => result
            }
            case head :: rest if ordering.compare(head, value) > 0 => go(rest, value, head :: acc)
            case _ :: rest => go(rest, value, acc)
          }
        }
        go(seq.toList, value, List.empty[T]).reverse
      }
      

      【讨论】:

      • 附加到任何 Seq 可能效率很低。我只会使用列表并预先添加。
      猜你喜欢
      • 1970-01-01
      • 2022-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-07
      • 1970-01-01
      • 1970-01-01
      • 2019-11-29
      相关资源
      最近更新 更多