How to add select line values ​​from a list in a functional style?

I solved my problem in an imperative style, but it looks very ugly. How can I do it better (more elegant, more concise, more functional - finally, its Scala). Lines with the same values ​​as the previous line, but with a different letter should be skipped, all other line values ​​should be added.

val row1 = new Row(20, "A", true) // add value val row2 = new Row(30, "A", true) // add value val row3 = new Row(40, "A", true) // add value val row4 = new Row(40, "B", true) // same value as the previous element & different letter -> skip row val row5 = new Row(60, "B", true) // add value val row6 = new Row(70, "B", true) // add value val row7 = new Row(70, "B", true) // same value as the previous element, but the same letter -> add value val rows = List(row1, row2, row3, row4, row5, row6, row7) var previousLetter = " " var previousValue = 0.00 var countSkip = 0 for (row <- rows) { if (row.value == previousValue && row.letter != previousLetter) { row.relevant = false countSkip += 1 } previousLetter = row.letter previousValue = row.value } // get sum val sumValue = rows.filter(_.relevant == true).map(_.value) reduceLeftOption(_ + _) val sum = sumValue match { case Some(d) => d case None => 0.00 } assert(sum == 290) assert(countSkip == 1) 

Thank you in advance

Twistleton

+4
source share
5 answers

Reduce:

 rows.reduceLeft { (prev, curr) => if (prev.value == curr.value && prev.letter != curr.letter) { curr.relevant = false countSkip += 1 } curr } 
+1
source
 (rows.head :: rows).sliding(2).collect{ case List(Row(v1,c1), Row(v2,c2)) if ! (v1 == v2 && c1 != c2) => v2 }.sum 
+9
source

I think the shortest (bulletproof) solution when Row is the case class (Boolean reset),

 (for ((Row(v1,c1), Row(v2,c2)) <- (rows zip rows.take(1) ::: rows) if (v1 != v2 || c1 == c2)) yield v1).sum 

Some of the other solutions do not handle the list-is-empty case, but this is largely because sliding has an error in which it will return an incomplete list if the list is too short. Clearer for me (as well as bulletproof):

 (rows zip rows.take(1) ::: rows).collect{ case (Row(v1,c1), Row(v2,c2)) if (v1 != v2 || c1 == c2) => v1 }.sum 

(it will only be two characters longer if you save it on one line). If you need to skip a number,

 val indicated = (rows zip rows.take(1) ::: rows).collect { case (Row(v1,c1), Row(v2,c2)) => (v1, v1 != v2 || c1 == c2) } val countSkip = indicated.filterNot(_._2).length val sum = indicated.filter(_._2).map(_._1).sum 
+6
source

Add it up:

 scala> rows.foldLeft((row1, 0))((p:(Row,Int), r:Row) => (r, p._2 + (if (p._1.value == r.value && p._1.letter != r.letter) 0 else r.value)))._2 res2: Int = 290 
+3
source
 (new Row(0, " ", true) +: rows).sliding(2).map { case List(r1, r2) => if (r1.value != r2.value || r1.letter == r2.letter) { r2.value } else { 0 } }.sum 

Of course, you can drop the Row logic if you don't need it for something else.

+2
source

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


All Articles