Scala: get the sum of the nth element from the tuple / RDD array

I have a tuple array like this:

 val a = Array((1,2,3), (2,3,4)) 

I want to write a generic method for a method as shown below:

 def sum2nd(aa: Array[(Int, Int, Int)]) = { aa.map { a => a._2 }.sum } 

So what I'm looking for, for example:

 def sumNth(aa: Array[(Int, Int, Int)], n: Int) 
+5
source share
3 answers

There are several ways to do this. The simplest is to use productElement :

 def unsafeSumNth[P <: Product](xs: Seq[P], n: Int): Int = xs.map(_.productElement(n).asInstanceOf[Int]).sum 

And then (note that indexing starts from zero, so n = 1 gives us the second element):

 scala> val a = Array((1, 2, 3), (2, 3, 4)) a: Array[(Int, Int, Int)] = Array((1,2,3), (2,3,4)) scala> unsafeSumNth(a, 1) res0: Int = 5 

This implementation may fail at runtime in two different ways:

 scala> unsafeSumNth(List((1, 2), (2, 3)), 3) java.lang.IndexOutOfBoundsException: 3 at ... scala> unsafeSumNth(List((1, "a"), (2, "b")), 1) java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at ... 

Ie if the tuple does not have enough elements, or if the requested element is not Int .

You can write a version that does not crash at runtime:

 import scala.util.Try def saferSumNth[P <: Product](xs: Seq[P], n: Int): Try[Int] = Try( xs.map(_.productElement(n).asInstanceOf[Int]).sum ) 

And then:

 scala> saferSumNth(a, 1) res4: scala.util.Try[Int] = Success(5) scala> saferSumNth(List((1, 2), (2, 3)), 3) res5: scala.util.Try[Int] = Failure(java.lang.IndexOutOfBoundsException: 3) scala> saferSumNth(List((1, "a"), (2, "b")), 1) res6: scala.util.Try[Int] = Failure(java.lang.ClassCastException: ... 

This is an improvement because it forces callers to solve the failure problem, but it is also annoying because it forces callers to eliminate the possibility of failure.

If you want to use Shapeless , you can get the best of both worlds:

 import shapeless._, shapeless.ops.tuple.At def sumNth[P <: Product](xs: Seq[P], n: Nat)(implicit atN: At.Aux[P, nN, Int] ): Int = xs.map(p => atN(p)).sum 

And then:

 scala> sumNth(a, 1) res7: Int = 5 

But the bad ones don't even compile:

 scala> sumNth(List((1, 2), (2, 3)), 3) <console>:17: error: could not find implicit value for parameter atN: ... 

This is still not ideal, as it means that the second argument must be a literal number (since it needs to be known at compile time):

 scala> val x = 1 x: Int = 1 scala> sumNth(a, x) <console>:19: error: Expression x does not evaluate to a non-negative Int literal sumNth(a, x) ^ 

In many cases this is not a problem.

To summarize: if you are willing to take responsibility for reasonable code that crashes your program, use productElement . If you want a little more security (due to some inconvenience), use productElement with Try . If you need compile-time security (but some limitations), use Shapeless.

+9
source

You can do something like this, although it is actually not safe:

  def sumNth(aa: Array[Product], n: Int)= { aa.map { a => a.productElement(n) match { case i:Int => i case _ => 0 } }.sum } sumNth(Array((1,2,3), (2,3,4)), 2) // 7 
+2
source

Antoher's typical way to do this without using shapeless is to provide a function to extract the required element:

 def sumNth[T, E: Numeric](array: Array[T])(extract: T => E) = array.map(extract).sum 

Then you can define sum2nd as follows:

 def sum2nd(array: Array[(Int, Int, Int)]): Int = sumNth(array)(_._2) 

Or like this:

 val sum2nd: Array[(Int, Int, Int)] => Int = sumNth(_)(_._2) 
+2
source

Source: https://habr.com/ru/post/1241844/


All Articles