[英]scala, filter a collection based on several conditions
我有一個代碼,例如:
val strs = List("hello", "andorra", "trab", "world")
def f1(s: String) = !s.startsWith("a")
def f2(s: String) = !s.endsWith("b")
val result = strs.filter(f1).filter(f2)
現在,應根據條件應用 f1 和 f2,例如:
val tmp1 = if (cond1) strs.filter(f1) else strs
val out = if (cond2) tmp1.filter(f2) else tmp1
有沒有更好的方法來做到這一點,而不使用臨時變量tmp1
?
一種方法是根據函數列表進行過濾,例如:
val fs = List(f1 _,f2 _)
fs.foldLeft(strs)((fn, list) => list.filter(fn))
但隨后我需要根據條件構建函數列表(因此,我會將使用臨時字符串列表變量的問題轉移到使用臨時函數列表變量(或者我應該需要使用可變列表) )。
我看起來像這樣(當然這不會編譯,否則我已經有了問題的答案):
val result =
strs
.if(cond1, filter(f1))
.if(cond2, filter(f2))
您可以使用隱式類為您提供以下語法:
val strs = List("hello", "andorra", "trab", "world")
def f1(s: String) = !s.startsWith("a")
def f2(s: String) = !s.endsWith("b")
val cond1 = true
val cond2 = true
implicit class FilterHelper[A](l: List[A]) {
def ifFilter(cond: Boolean, f: A => Boolean) = {
if (cond) l.filter(f) else l
}
}
strs
.ifFilter(cond1, f1)
.ifFilter(cond2, f2)
res1: List[String] = List(hello, world)
我會使用if
作為方法名稱,但它是保留字。
您可以通過對謂詞函數求和來做到這一點。
觀察過濾謂詞A => Boolean
有一個追加操作:
def append[A](p1: A => Boolean, p2: A => Boolean): A => Boolean =
a => p1(a) && p2(a)
和一個身份值:
def id[A]: A => Boolean =
_ => true
它滿足任何謂詞p: A => Boolean
, append(p, id) === p
。
這簡化了基於條件包含/排除謂詞的問題:如果條件為假,則只需包含id
謂詞。 它對過濾器沒有影響,因為它總是返回true
。
總結謂詞:
def sum[A](ps: List[A => Boolean]): A => Boolean =
ps.foldLeft[A => Boolean](id)(append)
請注意,我們折疊到id
上,因此如果ps
為空,我們將得到身份謂詞,即,如您所料,一個什么都不做的過濾器。
把這一切放在一起:
val predicates = List(cond1 -> f1 _, cond2 -> f2 _)
strs.filter(sum(predicates.collect { case (cond, p) if cond => p }))
// List(hello, world)
請注意,列表strs
僅被遍歷一次。
現在,對於上述的 Scalaz 版本:
val predicates = List(cond1 -> f1 _, cond2 -> f2 _)
strs filter predicates.foldMap {
case (cond, p) => cond ?? (p andThen (_.conjunction))
}
// List("hello", "world")
@Noah 的回答很好,如果您希望能夠對列表執行任何類型的操作,您可以接受並進一步概括它,然后在給定條件的情況下返回一個新列表,如果您進行以下更改:
implicit class FilterHelper[A](l: List[A]) {
def ifthen[B](cond: Boolean, f:(List[A]) => List[B]) = {
if (cond) f(l) else l
}
}
然后像這樣使用它:
val list = List("1", "2")
val l2 = list.ifthen(someCondition, _.filter(f1)
val l3 = list.ifthen(someOtherCondition, _.map(_.size))
將條件包含在過濾器的塊中會相當簡單,如下所示:
val result = strs filter (x => !cond1 || f1(x)) filter (x => !cond2 || f2(x))
如果滿足條件,結果將應用過濾器,或者簡單地返回相同的列表。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.