Scala集合:是否有安全的地图操作?

时间:2012-04-26 21:00:40

标签: scala scala-collections

假设我有一个List(或Map中的值),我想对每个项目执行操作。但不幸的是,无论出于何种原因,这个值列表都可以包含空值。

scala> val players = List("Messi", null, "Xavi", "Iniesta", null)
players: List[java.lang.String] = List(Messi, null, Xavi, Iniesta, null)

为了避免炸毁NPE,我需要做以下事情:

scala> players.filterNot(_ == null ).map(_.toUpperCase)
res84: List[java.lang.String] = List(MESSI, XAVI, INIESTA)

有没有更好的方法呢?

理想情况如下:

players.safeMap(_.toUpperCase)

scala-language mailing list上,西蒙提出了这个建议:

players.filter ( null !=).map(_.toUpperCase )

这是我原始版本的缩短版本,并且只要没有专用方法就可以获得。

更好的是,Stefan和Kevin提出了方法withFilter,它将返回一个惰性代理,因此两个操作都可以合并。

players.withFilter ( null !=).map(_.toUpperCase )

3 个答案:

答案 0 :(得分:3)

如果您无法避免null(例如,如果从Java代码中获取列表),另一种方法是使用collect代替map

scala> players.collect { case player if player != null => player.toUpperCase }
res0: List[java.lang.String] = List(MESSI, XAVI, INIESTA)

答案 1 :(得分:2)

我会这样做:

players flatMap Option map (_.toUpperCase)

但这比collect更糟糕。使用filtermap + collect总是更好。

答案 2 :(得分:1)

您可以转换为Option[String]的列表:

scala> val optionPlayers = players.map(Option(_))
optionPlayers: List[Option[java.lang.String]] = List(Some(Messi), None, Some(Xavi), Some(Iniesta), None)

Option普遍优先于null,它为您提供了如何安全处理数据的灵活性。以下是获得所需结果的简便方法:

scala> optionPlayers.collect { case Some(s) => s.toUpperCase }
res0: List[java.lang.String] = List(MESSI, XAVI, INIESTA)

scala> optionPlayers.flatMap(_.map(_.toUpperCase))
res1: List[java.lang.String] = List(MESSI, XAVI, INIESTA)

scala> optionPlayers.flatten.map(_.toUpperCase)
res2: List[java.lang.String] = List(MESSI, XAVI, INIESTA)

您可以在其他StackOverflow问题中或通过搜索网络找到有关Option的更多信息。

或者,您始终可以将safeMap方法定义为隐含在List上的implicit def enhanceList[T](list: List[T]) = new { def safeMap[R](f: T => R) = list.filterNot(_ == null).map(f) } 方法:

scala> players.safeMap(_.toUpperCase)
res4: List[java.lang.String] = List(MESSI, XAVI, INIESTA)

所以你可以这样做:

CanBuildFrom

虽然如果你定义一个隐式的,你可能想要使用像基本集合那样的List样式,使它不仅仅是{{1}}。您可以在其他地方找到更多相关信息。