第十章 Scala 容器基础(十七):使用filter方法过滤集合元素

原创
2016/04/13 15:07
阅读数 1.9W

Problem

    你想要筛选出集合中的一些元素形成一个新的集合,这些元素都是满足你的筛选条件的。

Solution

    在10.3节中,“选择一个集合方法来解决问题”,大量的方法可以被用来过滤输入集合的元素然后生成新的集合。这一节中展示了filter方法。那么如何正确使用集合的filter方法呢,首先你需要给filter方法一个判断条件或者返回true/false的函数,这个判断条件(函数)的输入类型要与集合元素类型一致,返回值是布尔型的。filter方法会对集合的每一个元素调用判断条件,当条件为true的时候则元素进入新的集合否则会被过滤掉。你还需要使用一个变量来指向新的集合。

    下面这个例子展示了,如何通过取模算法从一个输入集合中筛选出偶数并形成一个新的集合:

scala> val x = List.range(1, 10)
x: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> val evens = x.filter(_ % 2 == 0)
evens: List[Int] = List(2, 4, 6, 8)

    正如上面展示的,filter方法返回了所有使假设条件(_ % 2 == 0)为真的集合元素组成的新集合。还有一个方法filterNot,可以返回所有使假设条件返回false的元素组成的新集合。

scala> val evens = x.filterNot(_ % 2 == 0)
evens: List[Int] = List(1, 3, 5, 7, 9)

Discussion

    你可以使用的用来过滤集合主要方法已经在10.3节展示过了,在这里我们再来看一看都有什么:collect、diff、distinct、frop、dropWhile、filter、filterNot、find、foldLeft、foldRight、head、headOption、init、intersect、last、lastOption、reduceLeft、reduceRight、remove、slice、tail、take、takeWhile、union。

    filter方法对比其他方法的特点有:

  • filter方法遍历整个集合,其他的方法都只是遍历一部分元素

  • filter方法允许你提供一个判断条件(函数),来过滤集合元素

    如何筛选集合元素完全取决于你的算法,接下来的例子展示了一些方法来过滤字符串列表:

scala> val fruits = Set("orange", "peach", "apple", "banana")
fruits: scala.collection.immutable.Set[String] = Set(orange, peach, apple, banana)

scala> val x = fruits.filter(_.startsWith("a"))
x: scala.collection.immutable.Set[String] = Set(apple)

scala> val x = fruits.filter(_.length > 5)
x: scala.collection.immutable.Set[String] = Set(orange, banana)

    当你的判断逻辑复杂,没有办法一行写完,我们可以在filter内部使用多行的判断逻辑:

scala> val list = "apple" :: "banana" :: 1 :: 2 :: Nil
list: List[Any] = List(apple, banana, 1, 2)

scala> val strings = list.filter{
     |   case s:String => true
     |   case _ => false
     | }
strings: List[Any] = List(apple, banana)

    你同样可以定义一个判断函数,然后把这个函数传给filter方法:

def onlyStrings(a: Any) = a match {
  case s: String => true
  case _ => false
}

scala> val strings = list.filter(onlyStrings)
strings: List[Any] = List(apple, banana)

    接下来的这个例子告诉你,你可以多次连续调用filter方法:

def getFileContentsWithoutBlanksComments(canonicalFilename: String):
List[String] = {
  io.Source.fromFile(canonicalFilename)
    .getLines
    .toList
    .filter(_.trim != "")
    .filter(_.charAt(0) != '#')
}

    我们在一个文件中读取所有的行,转换为一个List,每行是一个元素,然后我们把空行过滤掉,然后再把#开头的过滤掉。看起来是一个统计shell脚本代码行数的算法。

    使用filter的两个关键点是:

  • 你的算法需要能正确判断出你所需要的元素,并返回true,对于你不需要的数据则返回false

  • 记得用一个新的变量指向filter方法返回的集合,因为filter方法并不会对原集合做改变

展开阅读全文
打赏
2
2 收藏
分享
加载中
更多评论
打赏
0 评论
2 收藏
2
分享
返回顶部
顶部