blue-sky
blue-sky

Reputation: 53806

Applying operation to corresponding elements of Array

I want to sum the corresponding elements of the list and multiply the results while keeping the label associated with the array element so

 ("a",Array((0.5,1.0),(0.667,2.0))) 

becomes :

(a , (0.5 + 0.667) * (1.0 + 2.0))

Here is my code to express this for a single array element :

val data = Array(("a",Array((0.5,1.0),(0.667,2.0))), ("b",Array((0.6,2.0), (0.6,2.0))))
                                                  //> data  : Array[(String, Array[(Double, Double)])] = Array((a,Array((0.5,1.0),
                                                  //|  (0.667,2.0))), (b,Array((0.6,2.0), (0.6,2.0))))

  val v1 = (data(0)._1, data(0)._2.map(m => m._1).sum)
                                                  //> v1  : (String, Double) = (a,1.167)
  val v2 = (data(0)._1, data(0)._2.map(m => m._2).sum)
                                                  //> v2  : (String, Double) = (a,3.0)

  val total = (v1._1 , (v1._2 * v2._2))           //> total  : (String, Double) = (a,3.5010000000000003)

I just want apply this function to all elements of the array so val "data" above becomes :

Map[(String, Double)] = ((a,3.5010000000000003),(b,4.8))

But I'm not sure how to combine the above code into a single function which maps over all the array elements ?

Update : the inner Array can be of variable length so this is also valid :

val data = Array(("a",Array((0.5,1.0,2.0),(0.667,2.0,1.0))), ("b",Array((0.6,2.0), (0.6,2.0))))

Upvotes: 1

Views: 148

Answers (3)

Shyamendra Solanki
Shyamendra Solanki

Reputation: 8851

You can use algebird numeric library:

val data = Array(("a",Array((0.5,1.0),(0.667,2.0))), ("b",Array((0.6,2.0), (0.6,2.0))))

import com.twitter.algebird.Operators._

def sumAndProduct(a: Array[(Double, Double)]) = {
    val sums = a.reduceLeft((m, n) => m + n)
    sums._1 * sums._2
}

data.map{ case (x, y) => (x, sumAndProduct(y)) }  
// Array((a,3.5010000000000003), (b,4.8))

It will work fine for variable size array as well.

val data = Array(("a",Array((0.5,1.0))), ("b",Array((0.6,2.0), (0.6,2.0))))
// Array((a,0.5), (b,4.8))

Upvotes: 1

Alex Cruise
Alex Cruise

Reputation: 7979

Pattern matching is your friend! You can use it for tuples and arrays. If there are always two elements in the inner array, you can do it this way:

val data = Array(("a",Array((0.5,1.0),(0.667,2.0))), ("b",Array((0.6,2.0), (0.6,2.0))))

data.map {
  case (s, Array((x1, x2), (x3, x4))) => s -> (x1 + x3) * (x2 + x4)
}
// Array[(String, Double)] = Array((a,3.5010000000000003), (b,4.8))

res6.toMap
// scala.collection.immutable.Map[String,Double] = Map(a -> 3.5010000000000003, b -> 4.8)

If the inner elements are variable length, you could do it this way (a for comprehension instead of explicit maps):

for {
  (s, tuples) <- data
  sum1 = tuples.map(_._1).sum
  sum2 = tuples.map(_._2).sum
} yield s -> sum1 * sum2

Note that while this is a very clear solution, it's not the most efficient possible, because we're iterating over the tuples twice. You could use a fold instead, but it would be much harder to read (for me anyway. :)

Finally, note that .sum will produce zero on an empty collection. If that's not what you want, you could do this instead:

val emptyDefault = 1.0 // Or whatever, depends on your use case

for {
  (s, tuples) <- data
  sum1 = tuples.map(_._1).reduceLeftOption(_ + _).getOrElse(emptyDefault)
  sum2 = tuples.map(_._2).reduceLeftOption(_ + _).getOrElse(emptyDefault)
} yield s -> sum1 * sum2

Upvotes: 1

Azzie
Azzie

Reputation: 366

Like this? Does your array always have only 2 pairs?

 val m = data map ({case (label,Array(a,b)) => (label, (a._1 + b._1) * (a._2 + b._2)) })
 m.toMap

Upvotes: 0

Related Questions