Search code examples
arraysscalaarraylistseqtake

Retrieving Values from a Seq based on a condition


I have the below Seq with me

scala>   var al = Seq((1.0, 20.0, 100.0), (2.0, 30.0, 100.0), (1.0, 11.0, 100.0), (1.0, 20.0, 100.0), (1.0, 10.0, 100.0),(2.0,9.0,100.0))
al: Seq[(Double, Double, Double)] = List((1.0,20.0,100.0), (2.0,30.0,100.0), (1.0,11.0,100.0), (1.0,20.0,100.0), (1.0,10.0,100.0), (2.0,9.0,100.0))

How to take from this Seq the first n elements where - sum of the second item is greater than 60 percent of the third item(this will be a constant value)

Expected Output -

scala> Seq((1.0, 20.0, 100.0), (2.0, 30.0, 100.0), (1.0, 11.0, 100.0))
res30: Seq[(Double, Double, Double)] = List((1.0,20.0,100.0), (2.0,30.0,100.0), (1.0,11.0,100.0))

Edit -1

I have done this with some ugly way. But if there is any beautiful functional way of solving this would be great. I would really need to get rid of this counters and is little bit tricky for me.

var counter =0d ; var sum = 0d 
var abc :Seq[Double] = for (x <- al) yield {
counter+=1 ;sum = sum + x._2 ; 
if (sum > 60) counter else 0
} 
println(al.take(abc.filterNot(_==0).min.toInt))

Solution

  • If you're on Scala 2.13.x then you might unfold():

    Seq.unfold((al,0.0)){ case (lst,sum) =>
      Option.when(lst.nonEmpty && sum < 0.6 * lst.head._3) {
        (lst.head, (lst.tail, sum + lst.head._2))
      }
    }
    //res0: Seq[(Double, Double, Double)] =
    // List((1.0,20.0,100.0), (2.0,30.0,100.0), (1.0,11.0,100.0))
    

    On earlier Scala versions you could scanLeft() to calculate the sums and use that to count how many elements to take().

    val cnt = al.view
                .scanLeft(0.0)(_ + _._2)
                .takeWhile(_ < 0.6 * al.headOption.getOrElse((0d,0d,0d))._3)
                .length
    al.take(cnt)