使用自定义逻辑过滤对象的 scala 集合
Posted
技术标签:
【中文标题】使用自定义逻辑过滤对象的 scala 集合【英文标题】:Filter scala collection of objects with custom logic 【发布时间】:2021-11-26 23:33:59 【问题描述】:我需要过滤一组对象(已经按属性 A 排序)删除属性 B 乱序的项目(下面用粗体标记): (为简单起见,一组元组)
案例一:
输入Seq((10, 10), (20, 20), (30, 36), (40, 30), (50, 40), (60, 50)) 预期输出:Seq((10, 10), (20, 20), (30, 36), (50, 40), (60, 50)) 实际输出:Seq((10,10), (20,20), (40,30), (50,40))案例2:
输入:Seq((10, 10), (20, 20), (30, 36), (40, 40), (50, 30), (60, 50)) 预期输出:Seq((10, 10), (20, 20), (30, 36), (40, 40), (60, 50)) 实际输出:Seq((10,10), (20,20), (30,36), (50,30))我将不胜感激。
这是我目前所拥有的:
@RunWith(classOf[SpringRunner])
class OutOfOrderTest extends AnyWordSpec with Matchers with Debuggable
"OutOfOrderTest" should
def ooo(a: Seq[(Int, Int)]): Option[(Int, Int)] =
val x = a.head
val y = a.tail.head
if (x._2 <= y._2) Some(x) else None
"filter out of order simple" in
val bad = Seq((10, 10), (20, 20), (30, 36), (40, 30), (50, 40), (60, 50))
val filtered = bad.sliding(2).collect a =>
ooo(a)
.filter(_.isDefined).map(_.get).toSeq
filtered shouldEqual Seq((10, 10), (20, 20), (30, 36), (50, 40), (60, 50))
"filter out of order complex" in
val bad2 = Seq((10, 10), (20, 20), (30, 36), (40, 40), (50, 30), (60, 50))
val filtered2 = bad2.sliding(2).collect a =>
ooo(a)
.filter(_.isDefined).map(_.get).toSeq
filtered2 shouldEqual Seq((10, 10), (20, 20), (30, 36), (40, 40), (60, 50))
更新 1
我使用序列来提高性能(可能最好使用数组,但我可以稍后处理)
为简单起见,我在这里使用了元组,但它们实际上是案例类 (Target) 的实例,其中 2 个字段由元组数据表示。
这也是一个更大的 Spring-boot 应用程序中的一个过滤器,我必须用它制作一个组件。
虽然“组件化”(是的,我们在办公室里提出了这个词)过滤器没有问题,但来自@Luis 的递归函数将难以抽象如下:
我想将这三种方法作为组件进行测试 因此,如果可以的话,我会将 3 个答案标记为有效。
case class Target(rt: Double, ri: Double)
trait OutOfOrderRemover
def filter(targets: Seq[Target])
@Component
class RecurtsiveOOO extends OutOfOrderRemover
override def filter(targets: Seq[Target]): Seq[Target] =
【问题讨论】:
感谢所有 jwvh、Guru 和 Luis 的快速回答,每个人都有我喜欢的东西,jwvh 非常简洁,Guru 使用折叠,Luis 使用递归。 【参考方案1】:这是我的 2 美分。
这将为已发布的两个 input
示例返回所需的结果。
input.head +: input.sliding(2).collect
case Seq((_,b1),(a2,b2)) if b1 <= b2 => (a2,b2)
.toSeq
警告:如果input
为空,将抛出。
【讨论】:
【参考方案2】:恕我直言,这是尾递归的完美用例。
def removeUnsortedElements[A, B](data: List[A])(orderBy: A => B)(implicit ev: Ordering[B]): List[A] =
import Ordering.Implicits._
@annotation.tailrec
def loop(remaining: List[A], previous: B, acc: List[A]): List[A] =
remaining match
case a :: tail =>
val next = orderBy(a)
val newAcc =
if (next >= previous) a :: acc
else acc
loop(
remaining = tail,
previous = next,
acc = newAcc
)
case Nil =>
acc.reverse
data match
case first :: tail =>
loop(
remaining = tail,
previous = orderBy(first),
acc = first :: Nil
)
case Nil =>
Nil
可以这样使用:
val input1 = List((10, 10), (20, 20), (30, 36), (40, 30), (50, 40), (60, 50))
val output1 = removeUnsortedElements(input1)(_._2)
// output1: List[Int] = List((10,10), (20,20), (30,36), (50,40), (60,50))
val input2 = List((10, 10), (20, 20), (30, 36), (40, 40), (50, 30), (60, 50))
val output2 = removeUnsortedElements(input2)(_._2)
// output2: List[Int] = List((10,10), (20,20), (30,36), (40,40), (60,50))
可以看到运行here的代码。
【讨论】:
【参考方案3】:您可以将foldLeft
与空集合累加器一起使用,将当前值与累加器头部1进行比较,如果满足条件则追加当前值,然后反转结果:
val bad = ...
val filtered = bad.foldLeft(List.empty[(Int, Int)])((acc, t) => acc match
case head :: _ if head._2 > t._2 => acc
case _ => t :: acc
)
.reverse
【讨论】:
以上是关于使用自定义逻辑过滤对象的 scala 集合的主要内容,如果未能解决你的问题,请参考以下文章
在 AADAppRoleStatelessAuthenticationFilter 中添加自定义逻辑,以便我可以决定过滤哪些请求