如何根据条件从 Scala 中的地图中提取列表?
How do I extract a list from a map in Scala according to conditions?
我有一个由字符串和元组列表组成的映射:
Map[String, List[(String, Int]]
地图中的一些线条示例如下:
"test1", List(("apple", 0), ("pear", 1), ("banana", 0))
"test2", List(("green", 1), ("yellow", 1), ("red", 0))
"test3", List(("monday", 0), ("tuesday", 1), ("wednesday", 0))
仅当元组列表包含超过 1 个等于 0 的值时,提取每行中第一个字符串列表的最佳方法是什么?
换句话说,我想要一个包含“test1”和“test2”的列表,因为这两行是唯一在其元组列表中包含超过 1 个“0”的行。
您的起点将是 collect
或 filter
;当你有一个集合并且你想要一个应用了一些条件的新集合时,这两种方法都很有用。
如前所述,您的条件听起来像是 count
方法的 use-case,标准库中的几乎所有集合类型都定义了该方法。
这应该有效...
theMapFromYourExample
.collect { case (key, list) if list.count(_._2 == 0) > 1 => key }
.toList
...但有一些注意事项:
.collect
和 toList
都将创建新的整个集合。将它们放在一起意味着您正在浪费一些内存和 CPU 时间,因为您构建了一个中间集合,一旦您的表达式完成 运行,它就会直接发送到垃圾收集。为避免为整个中间集合分配内存,您可以使用 View 模式,可通过大多数集合的 .view
方法访问。
.count
是列表大小的 O(N),因为它需要检查列表中的每个项目以查看它是否符合您的条件。由于您只关心计数“至少”是某个特定值,因此如果超过 1,您可以提前停止计数。lengthCompare
方法对这种事情很有用。
下面是大致相同的代码,利用“视图”来提高效率:
def hasAtLeastTwoZeroes(list: List[(String, Int)]) = {
list
.view // lets us call `.filter` without allocating a new List
.filter(_._2 == 0) // apply conditional: value is 0
.lengthCompare(1) > 0 // like saying `.length > 1`, but is O(1), not O(N)
}
theMapFromYourExample
.view // lets us call `collect` without allocating an intermediate collection
.collect { case (key, list) if hasAtLeastTwoZeroes(list) => key }
.toList // builds a List from the collected view
Update 正如 Luis 在评论中指出的那样,Scala 2.13 添加了 sizeIs
方法,它具有比 lengthCompare
[= 更流畅的界面25=]
我有一个由字符串和元组列表组成的映射:
Map[String, List[(String, Int]]
地图中的一些线条示例如下:
"test1", List(("apple", 0), ("pear", 1), ("banana", 0))
"test2", List(("green", 1), ("yellow", 1), ("red", 0))
"test3", List(("monday", 0), ("tuesday", 1), ("wednesday", 0))
仅当元组列表包含超过 1 个等于 0 的值时,提取每行中第一个字符串列表的最佳方法是什么?
换句话说,我想要一个包含“test1”和“test2”的列表,因为这两行是唯一在其元组列表中包含超过 1 个“0”的行。
您的起点将是 collect
或 filter
;当你有一个集合并且你想要一个应用了一些条件的新集合时,这两种方法都很有用。
如前所述,您的条件听起来像是 count
方法的 use-case,标准库中的几乎所有集合类型都定义了该方法。
这应该有效...
theMapFromYourExample
.collect { case (key, list) if list.count(_._2 == 0) > 1 => key }
.toList
...但有一些注意事项:
.collect
和toList
都将创建新的整个集合。将它们放在一起意味着您正在浪费一些内存和 CPU 时间,因为您构建了一个中间集合,一旦您的表达式完成 运行,它就会直接发送到垃圾收集。为避免为整个中间集合分配内存,您可以使用 View 模式,可通过大多数集合的.view
方法访问。.count
是列表大小的 O(N),因为它需要检查列表中的每个项目以查看它是否符合您的条件。由于您只关心计数“至少”是某个特定值,因此如果超过 1,您可以提前停止计数。lengthCompare
方法对这种事情很有用。
下面是大致相同的代码,利用“视图”来提高效率:
def hasAtLeastTwoZeroes(list: List[(String, Int)]) = {
list
.view // lets us call `.filter` without allocating a new List
.filter(_._2 == 0) // apply conditional: value is 0
.lengthCompare(1) > 0 // like saying `.length > 1`, but is O(1), not O(N)
}
theMapFromYourExample
.view // lets us call `collect` without allocating an intermediate collection
.collect { case (key, list) if hasAtLeastTwoZeroes(list) => key }
.toList // builds a List from the collected view
Update 正如 Luis 在评论中指出的那样,Scala 2.13 添加了 sizeIs
方法,它具有比 lengthCompare
[= 更流畅的界面25=]