Ensure list contains only specific values - scala

How can I make sure a list only contains a specific set of items?
List[Int]
A function to make sure the list only contains the values 10, 20 or 30.
I'm sure this is built in by I can't find it!

Your question doesn't specify what you want to happen when the list doesn't contain the requisite items.
The following will return true if all the items in the List match your criteria, false otherwise:
val ints1: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
val ints2: List[Int] = List(10, 10, 10, 10)
ints1.forall(i => List(10, 20, 30).contains(i)) // false
ints2.forall(i => List(10, 20, 30).contains(i)) // true
The following will return a List with only those items which matched the criteria:
val ints1: List[Int] = List(10, 20, 30, 40, 50, 60, 70)
val ints2: List[Int] = List(10, 10, 10)
ints1.filter(i => List(10, 20, 30).contains(i)) // List(10, 20, 30)
ints2.filter(i => List(10, 20, 30).contains(i)) // List(10, 10, 10)

forall
You may use forall with a Set containing elements which are valid or legal and you want to see in the list.
list.forall(Set(10, 20, 30).contains) //true means list only contains 10, 20, 30
Set is Function
You need not use contains method as Set extends Int => Boolean. You can use Set like a function
list forall Set(10, 20, 30)
Filter
You can use filter to filter out the elements which are not in the given list. Again you can use Set as function as Set extends Function.
list.filter(Set(10, 20, 30)).nonEmpty //true means list only contains 10, 20 and 30
Collect if you like pattern matching
Collect takes a Partial function. If you like pattern matching just use collect
list.collect {
case 10 => 10
case 20 => 20
case 30 => 30
}.nonEmpty //true means list only contains 10, 20 and 30
Scala REPL
scala> val list = List(10, 20, 30, 40, 50)
list: List[Int] = List(10, 20, 30, 40, 50)
scala> list forall Set(10, 20, 30)
res6: Boolean = false

If you simply want to determine whether all of the values in your list are "legal", use forall:
def isLegal(i: Int): Boolean = ??? // e.g. is it 10, 20, or 30
val allLegal = list forall isLegal
If you want to trim down your list so that only legal values remain, use filter:
val onlyLegalValues = list filter isLegal
Note that a Set[Int] counts as a Int => Boolean function, so you could use that in place of your isLegal method:
val isLegal = Set(10, 20, 30)
val allLegal = list forall isLegal
val onlyLegalValues = list filter isLegal

Related

Getting maximum value in remaining sub-list for each element in a list

val input = List(16, 17, 4, 5, 3, 0)
We have to sort in a way where starting from the first element we need to return the maximum element in the remaining list.
I want output like 17,5,5,3,0.
I've Tried Below code
val v1 = input.scanRight(Int.MinValue)(math.max).dropRight(1)
println("variation 1, with scan")
println(v1)
If I understand your requirement correctly, you could use foldRight to traverse the list from right to left, and at each iteration store the maximum value of the traversed elements in the accumulator, which is a Tuple of (List[Int], Int):
val input = List(16, 17, 4, 5, 3, 0)
input.foldRight((List[Int](), Int.MinValue)){ case (i, (ls, j)) =>
val m = i max j
(m :: ls, m)
}._1
// res1: List[Int] = List(17, 17, 5, 5, 3, 0)
Reversing will work in above case
var max = Int.MinValue
val buffer = new scala.collection.mutable.ArrayBuffer[Int](input.length)
for (i <- input.reverse if i >= max) {
max = i
i +=: buffer
}
println(buffer.toList)

Scala - Scanleft, returning values within iterations without using it in a next turn

I have a list
what I would like to do is
def someRandomMethod(...): ... = {
val list = List(1, 2, 3, 4, 5)
if(list.isEmpty) return list
list.differentScanLeft(list.head)((a, b) => {
a * b
})
}
which returns
List(1, 2, 6, 12, 20) rather than List(1, 2, 6, 24, 120)
Is there such API?
Thank you,
Cosmir
scan is not really the right method for this, you want to use sliding to generate a list of adjacent pairs of values:
(1::list).sliding(2).map(l => l(0)*l(1))
More generally, it is sometimes necessary to pass data on to the next iteration when using scan. The standard solution to this is to use a tuple (state, ans) and then filter out the state with another map at the end.
You probably want to use zip
val list = List(1,2,3,4,5,6)
val result = list.zip(list.drop(1)).map{case (a,b) => a*b}
println(result)
> List(2, 6, 12, 20, 30)

Cumulative sum without var scala [duplicate]

I've got a List of days in the month:
val days = List(31, 28, 31, ...)
I need to return a List with the cumulative sum of days:
val cumDays = List(31, 59, 90)
I've thought of using the fold operator:
(0 /: days)(_ + _)
but this will only return the final result (365), whereas I need the list of intermediate results.
Anyway I can do that elegantly?
Scala 2.8 has the methods scanLeft and scanRight which do exactly that.
For 2.7 you can define your own scanLeft like this:
def scanLeft[a,b](xs:Iterable[a])(s:b)(f : (b,a) => b) =
xs.foldLeft(List(s))( (acc,x) => f(acc(0), x) :: acc).reverse
And then use it like this:
scala> scanLeft(List(1,2,3))(0)(_+_)
res1: List[Int] = List(0, 1, 3, 6)
I'm not sure why everybody seems to insist on using some kind of folding, while you basically want to map the values to the cumulated values...
val daysInMonths = List(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
val cumulated = daysInMonths.map{var s = 0; d => {s += d; s}}
//--> List[Int] = List(31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)
You can simply perform it:
daysInMonths.foldLeft((0, List[Int]()))
{(acu,i)=>(i+acu._1, i+acu._1 :: acu._2)}._2.reverse
Fold into a list instead of an integer. Use pair (partial list with the accumulated values, accumulator with the last sum) as state in the fold.
Fold your list into a new list. On each iteration, append a value which is the sum of the head + the next input. Then reverse the entire thing.
scala> val daysInMonths = List(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
daysInMonths: List[Int] = List(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
scala> daysInMonths.foldLeft(Nil: List[Int]) { (acc,next) =>
| acc.firstOption.map(_+next).getOrElse(next) :: acc
| }.reverse
res1: List[Int] = List(31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)
You can also create a monoid class that concatenates two lists while adding to the second one the last value from the first. No mutables and no folds involved:
case class CumSum(v: List[Int]) { def +(o: CumSum) = CumSum(v ::: (o.v map (_ + v.last))) }
defined class CumSum
scala> List(1,2,3,4,5,6) map {v => CumSum(List(v))} reduce (_ + _)
res27: CumSum = CumSum(List(1, 3, 6, 10, 15, 21))
For any:
val s:Seq[Int] = ...
You can use one of those:
s.tail.scanLeft(s.head)(_ + _)
s.scanLeft(0)(_ + _).tail
or folds proposed in other answers but...
be aware that Landei's solution is tricky and you should avoid it.
BE AWARE
s.map { var s = 0; d => {s += d; s}}
//works as long `s` is strict collection
val s2:Seq[Int] = s.view //still seen as Seq[Int]
s2.map { var s = 0; d => {s += d; s}}
//makes really weird things!
//Each value'll be different whenever you'll access it!
I should warn about this as a comment below Landei's answer but I couldn't :(.
Works on 2.7.7:
def stepSum (sums: List [Int], steps: List [Int]) : List [Int] = steps match {
case Nil => sums.reverse.tail
case x :: xs => stepSum (sums.head + x :: sums, steps.tail) }
days
res10: List[Int] = List(31, 28, 31, 30, 31)
stepSum (List (0), days)
res11: List[Int] = List(31, 59, 90, 120, 151)

accumulate list based on a condition using dropWhile

val list = List("1","10","12","30","40","50")
based on parameter n in eg "12" here , the elements ahead of "12" should form a
list List("30,40,50") and final list should be created as below
Expected Output
List("1","10","12",List("30,40,50") )
list.dropWhile(_!="12").tail gives `List("30,40,50")` but i am not above the achieve the desired output
partition will give the closest output to what you are looking for.
scala> list.partition(_ <= "12")
res21: (List[String], List[String]) = (List(1, 10, 12),List(30, 40, 50))
All elements of List must have the same type. splitAt or partition can accomplish this albeit with a different return type than you want. I suspect that the desired return type List[String, ..., List[String]] is a "type smell" that may indicate a another issue.
Maybe span could help:
val (a, b) = list.span(_ != "12")
val h :: t = b
val res = a :+ h :+ List(t.mkString(","))
produces for input List("123", "10", "12", "30", "40", "50"):
List("123", "10", "12", List("30,40,50"))
If you handle the output type(which is List[java.io.Serializable]), here is the acc method, which takes the desired parameter, a String s in this case :
def acc(list:List[String],s:String) = {
val i = list.indexOf(s)
list.take(i+1):+List(list.drop(i+1).mkString(","))
}
In Scala REPL:
scala> val list = List("1","10","12","30","40","50")
list: List[String] = List(1, 10, 12, 30, 40, 50)
scala> acc(list,"12")
res29: List[java.io.Serializable] = List(1, 10, 12, List(30,40,50))
scala> acc(list,"10")
res30: List[java.io.Serializable] = List(1, 10, List(12,30,40,50))
scala> acc(list,"40")
res31: List[java.io.Serializable] = List(1, 10, 12, 30, 40, List(50))
scala> acc(list,"30")
res32: List[java.io.Serializable] = List(1, 10, 12, 30, List(40,50))

Concatenating multiple lists in Scala

I have a function called generateList and concat function as follows. It is essentially concatenating lists returned by the generateList with i starting at 24 and ending at 1
def concat(i: Int, l: List[(String, Int)]) : List[(String, Int)] = {
if (i==1) l else l ::: concat(i-1, generateList(signs, i))
}
val all = concat(23, generateList(signs, 24))
I can convert this to tail-recursion. But I am curious if there a scala way of doing this?
There are many ways to do this with Scala's built in methods available to Lists.
Here is one approach that uses foldRight
(1 to 24).foldRight(List[Int]())( (i, l) => l ::: generateList(i))
Starting with the range of ints you use to build separate lists, it concats the result of generateList(i) to the initial empty list.
Here is one way to do this:
val signs = ""
def generateList(s: String, n: Int) = n :: n * 2 :: Nil
scala> (24 to 1 by -1) flatMap (generateList(signs, _))
res2: scala.collection.immutable.IndexedSeq[Int] = Vector(24, 48, 23, 46, 22, 44, 21, 42, 20, 40, 19, 38, 18, 36, 17, 34, 16, 32, 15, 30, 14, 28, 13, 26, 12, 24, 11, 22, 10, 20, 9, 18, 8, 16, 7, 14, 6, 12, 5, 10, 4, 8, 3, 6, 2, 4, 1, 2)
What you want to do is to map the list with x => generateList(signs, x) function and then concatenate the results, i.e. flatten the list. This is just what flatMap does.