Spark:总结包含None和Some()的列表?

时间:2017-04-18 02:27:08

标签: scala apache-spark

我已经明白,我可以使用List.sum轻松地总结一个列表:

var mylist = List(1,2,3,4,5)

mylist.sum
// res387: Int = 15

但是,我有一个包含NoneSome(1)等元素的列表。这些值是在运行左外连接后生成的。

现在,当我尝试运行List.sum时,出现错误:

var mylist= List(Some(0), None, Some(0), Some(0), Some(1))

mylist.sum
<console>:27: error: could not find implicit value for parameter num: Numeric[Option[Int]]
       mylist.sum
              ^

如何解决此问题?我可以以某种方式将NoneSome值转换为整数,也许就在左外连接之后?

5 个答案:

答案 0 :(得分:9)

您可以使用List.collect方法进行模式匹配:

mylist.collect{ case Some(x) => x }.sum
// res9: Int = 1

这会忽略None元素。

另一种选择是使用getOrElse上的Option来提取值,在这里您可以选择要替换None的值:

mylist.map(_.getOrElse(0)).sum
// res10: Int = 1

答案 1 :(得分:7)

我发现处理Option[A]集合的最简单方法是flatten

val myList = List(Some(0), None, Some(0), Some(0), Some(1))
myList.flatten.sum

flatten的调用将删除所有None个值,并将剩余的Some[Int]转换为普通的Int - 最终为您留下Int的集合}。

顺便说一下,拥抱不变性是Scala中的一等公民,并且更喜欢valvar

答案 2 :(得分:2)

如果您想避免使用flattenmap创建额外的中间集合,则应考虑使用Iterator,例如

mylist.iterator.flatten.sum

mylist.iterator.collect({ case Some(x) => x }).sum

mylist.iterator.map(_.getOrElse(0)).sum

我认为第一种和第二种方法更好一点,因为它们可以避免不必要的添加0.我可能会采用第一种方法,因为它很简单。

如果您想要有点花哨(或需要额外的通用性),您可以定义自己的Numeric[Option[Int]]实例。类似这样的内容适用于Option[N]类型N类型Numeric本身有Option[Int]个实例,即Option[Double]Option[BigInt]Option[Option[Int]],{ {1}}等等。

implicit def optionNumeric[N](implicit num: Numeric[N]) = {
  new Numeric[Option[N]] {
    def compare(x: Option[N], y: Option[N]) = ??? //left as an exercise :-)
    def fromInt(x: Int) = if (x != 0) Some(num.fromInt(x)) else None
    def minus(x: Option[N], y: Option[N]) = x.map(vx => y.map(num.minus(vx, _)).getOrElse(vx)).orElse(negate(y))
    def negate(x: Option[N]) = x.map(num.negate(_))
    def plus(x: Option[N], y: Option[N]) = x.map(vx => y.map(num.plus(vx, _)).getOrElse(vx)).orElse(y)
    def times(x: Option[N], y: Option[N]) = x.flatMap(vx => y.map(num.times(vx, _)))
    def toDouble(x: Option[N]) = x.map(num.toDouble(_)).getOrElse(0d)
    def toFloat(x: Option[N]) = x.map(num.toFloat(_)).getOrElse(0f)
    def toInt(x: Option[N]) = x.map(num.toInt(_)).getOrElse(0)
    def toLong(x: Option[N]) = x.map(num.toLong(_)).getOrElse(0L)
    override val zero = None
    override val one = Some(num.one)
  }
}

示例:

List(Some(3), None, None, Some(5), Some(1), None).sum
//Some(9)

List[Option[Int]](Some(2), Some(4)).product
//Some(8)

List(Some(2), Some(4), None).product
//None

List(Some(Some(3)), Some(None), Some(Some(5)), None, Some(Some(1)), Some(None)).sum
//Some(Some(9))

List[Option[Option[Int]]](Some(Some(2)), Some(Some(4))).product
//Some(Some(8))

List[Option[Option[Int]]](Some(Some(2)), Some(Some(4)), None).product
//None

List[Option[Option[Int]]](Some(Some(2)), Some(Some(4)), Some(None)).product
//Some(None) !?!?!

请注意,可能存在多种表示“零”的方式,例如NoneSome(0) Option[Int]None,{{1}}优先{{1}}。另外,请注意这种方法包含了如何将半群(没有附加身份)变成幺半群的基本思想。

答案 3 :(得分:0)

您可以使用.fold或.reduce手动实现2个选项的总和。但我会去@Psidom approach

答案 4 :(得分:0)

列表上的折叠是一种更优化的解决方案。要小心在集合上链接函数调用,因为您可能会多次迭代List之类的内容。

更优化的方法看起来像

val foo = List(Some(1), Some(2), None, Some(3))

foo.foldLeft(0)((acc, optNum) => acc + optNum.getOrElse(0))