Using if inside of fold - if-statement

I need to count the length of a vector of (bool, i32) where if the bool is true I increment count. I'm using fold to do this:
fn main() {
let domain = [(true, 1), (false, 2), (true, 3)];
let dom_count = domain.iter()
.fold(0, |count, &(exists, _)| if exists {count + 1});
println!("dom_count: {}", dom_count);
}
The compiler complained saying:
.fold(0, |count, &(exists, _)| if exists {count + 1})
^^^^^^^^^^^^^^^^^^^^^ expected (), found integral variable
So I added a ; and got this:
.fold(0, |count, &(exists, _)| if exists {count + 1;})
^^^^^^^^^^^^^^^^^^^^^^ expected integral variable, found ()
How do you correctly use an if statement inside of fold?

What value are you attempting to use when the if condition is false?
That's what the compiler is telling you first. Because there's no else clause, the return type of the missing clause must be (). And since both the true and false branches of the if must have the same type, the true branch must return (). However, your true branch is trying to return a number.
By adding the ;, you made it so that both branches of the if returned (), which then failed because your fold is supposed to return an integer.
One solution is to return a value in the else clause:
fn main() {
let domain = [(true, 1), (false, 2), (true, 3)];
let dom_count = domain.iter()
.fold(0, |count, &(exists, _)| {
if exists {
count + 1
} else {
count
}
});
println!("dom_count: {}", dom_count);
}
Or
fn main() {
let domain = [(true, 1), (false, 2), (true, 3)];
let dom_count = domain.iter()
.fold(0, |count, &(exists, _)| {
count + if exists {
1
} else {
0
}
});
println!("dom_count: {}", dom_count);
}
It's much more idiomatic to use filter:
fn main() {
let domain = [(true, 1), (false, 2), (true, 3)];
let dom_count = domain.iter()
.filter(|&&(exists, _)| exists)
.fold(0, |count, _| count + 1);
println!("dom_count: {}", dom_count);
}
And the act of counting the the number of items is already handled by Iterator::count:
fn main() {
let domain = [(true, 1), (false, 2), (true, 3)];
let dom_count = domain.iter().filter(|&&(exists, _)| exists).count();
println!("dom_count: {}", dom_count);
}

Related

Scala: slice a list from the first non-zero element

Suppose I have a list filled with zeroes
val a = List(0,0,0,0,2,4,0,6,0,7)
I want to slice away the zeroes preceding the first non-zero element and also return the index where the 1st non-zero element is present.
Foe the above case I want an output:
output = List(2,4,0,6,0,7)
idx = 4
How do I do it?
First, you can use zipWithIndex to conveniently pair each element with its index. Then use dropWhile to return all of the preceding zero elements. From there, you'll have all of the remaining elements paired with their indices from the original List. You can unzip them. Since this may result in an empty list, the index you're looking for should be optional.
scala> val (remaining, indices) = a.zipWithIndex.dropWhile { case (a, i) => a == 0 }.unzip
remaining: List[Int] = List(2, 4, 0, 6, 0, 7) // <--- The list you want
indices: List[Int] = List(4, 5, 6, 7, 8, 9)
scala> val index = indices.headOption
index: Option[Int] = Some(4) // <--- the index of the first non-zero element
This is a use-case for span:
val a = List(0,0,0,0,2,4,0,6,0,7)
val (zeros, output) = a.span(_ == 0)
val idx = zeros.length
use dropWhile:
val output = a.dropWhile{ _ == 0 }
val idx = output.headOption
.map(_ => a.length - output.length)
.getOrElse(-1) // index starting from 0, -1 if not found
Sightly modified from #bottaio answer, but returning an Option[Int] instead of a plain Int for the index.
def firstNonZero(l: List[Int]): (Option[Int], List[Int]) = {
#annotation.tailrec
def go(remaining: List[Int], idx: Int): (Int, List[Int]) =
remaining match {
case Nil => idx -> Nil
case 0 :: xs => go(remaining = xs, idx + 1)
case xs => idx -> xs
}
l match {
case 0 :: xs =>
val (idx, list) = go(remaining = xs, idx = 1)
Some(idx) -> list
case list =>
None -> list
}
}
Just another solution using foldLeft:
val (i, l) =
a.foldLeft((None: Option[Int], List.empty: List[Int]))((b, n) => {
if (n == 0 && b._2.isEmpty) (b._1.orElse(Some(0)).map(_ + 1), List.empty)
else (b._1.orElse(Some(0)), b._2 :+ n)
})
i: Option[Int] = Some(4)
l: List[Int] = List(2, 4, 0, 6, 0, 7)
You can do it pretty clean with indexWhere:
val idx = a.indexWhere(_!=0)
val output = a.drop(idx)
Others have provided answers that requires multiple list traversals. You can write a recursive function to calculate that in a single pass:
def firstNonZero(l: List[Int]): (Int, List[Int]) = {
#tailrec
def go(l: List[Int], idx: Int): (Int, List[Int]) = l match {
case Nil => (idx, Nil)
case 0 :: xs => go(xs, idx + 1)
case xs => (idx, xs)
}
go(l, 0)
}
what is also equivalent to
val (leadingZeros, rest) = a.span(_ == 0)
val (index, output) = (leadingZeros.length, rest)

Combining two lists of objects into one based on a business logic with Scala

In continuation of Scala learning curve
I have two lists of objects. I need to merge these lists into one list, while applying a logic with matching pares.
So, for example, here are the two lists:
case class test(int: Int, str: String)
val obj1 = test(1, "one")
val obj2 = test(2, "two")
val list1 = List(obj1, obj2)
val obj3 = test(2, "Another two")
val obj4 = test(4, "four")
val list2 = List(obj1, obj2)
What I need is:
List(test(1, "one old"), test(2, "Another two updated"), test(4, "four new"))
Of coarse, I can iterate though all elements in an old fashioned way, and do all the conversions there, but that is not the "Scala way" (I guess).
I tried approaching it with foldLeft, but got stuck. Here is what I have that is not working:
list1.foldLeft(list2) { (a:test, b:test) =>
b.int match {
case a.int => {
//Apply logic and create new object
}
}
}
UPDATE
For now I did it in two steps:
var tasks : Seq[ChecklistSchema.Task] = left.tasks.map((task:ChecklistSchema.Task) =>
right.tasks.find(t => t.groupId == task.groupId) match {
case Some(t: ChecklistSchema.Task) => t
case _ => {
task.status match {
case TaskAndValueStatus.Active => task.copy(status = TaskAndValueStatus.Hidden)
case _ => task
}
}
}
)
tasks = tasks ++ right.tasks.filter((t:ChecklistSchema.Task) => !tasks.contains(t))
There is got to be a better approach!
Thanks,
*Assuming val list2 = List(obj3, obj4).
Here's my approach to this:
Apply "old" to all list1 entries
Create a map for list2 in order to efficiently check (in the next method) if a duplicated value came from list2. (breakOut here instructs the compiler to build it using the most appropriate factory. More at https://stackoverflow.com/a/7404582/4402547)
applyLogic decides what to call a not-old test ("new" or "updated")
Put them together, groupBy on the index, applyLogic, and sort (optional).
def merge(left: List[Test], right: List[Test]) = {
val old = list1.map(t => Test(t.int, t.str+" old"))
val l2Map = list2.map(t => (t.int -> t)) (breakOut): Map[Int, Test]
def applyLogic(idx: Int, tests: List[Test]): Test = {
tests.size match {
case 1 => {
val test = tests.head
if(l2Map.contains(test.int)) Test(test.int, test.str + " new") else test
}
case 2 => {
val updated = tests(1)
Test(updated.int, updated.str+" updated")
}
}
}
(old ++ list2).groupBy(t => t.int).map(f => applyLogic(f._1, f._2)).toList.sortBy((t => t.int))
}
val left = List(Test(1, "one"), Test(2, "two"))
val right = List(Test(2, "Another two"), Test(4, "four"))
val result = List(Test(1, "one old"), Test(2, "Another two updated"), Test(4, "four new"))
assert(merge(left, right) == result)
I don't know if this solution is "Scala way" but it is using foldLeft.
case class Test(a: Int, b: String) {
def labeled(label: String) = copy(b = b + " " + label)
}
def merge(left: List[Test], right: List[Test]) = {
val (list, updated) = left.foldLeft((List[Test](), Set[Int]())) { case ((acc, founded), value) =>
right.find(_.a == value.a) match {
case Some(newValue) => (newValue.labeled("updated") :: acc, founded + value.a)
case None => (value.labeled("old") :: acc, founded)
}
}
list.reverse ::: right.filterNot(test => updated(test.a)).map(_.labeled("new"))
}
val left = List(Test(1, "one"), Test(2, "two"))
val right = List(Test(2, "Another two"), Test(4, "four"))
val result = List(Test(1, "one old"), Test(2, "Another two updated"), Test(4, "four new"))
assert(merge(left, right) == result)
(list1 ++ (list2.map(l => l.copy(str = l.str + " new")))).groupBy(_.int).map(
l =>
if (l._2.size >= 2) {
test(l._2(0).int, "Another two updated")
} else l._2(0)
)
map to update new value and use groupBy to update distinct value

Function always return Nil

I am trying to resolve some anagrams assignments. And I can't figure out the problem behind getting always a List() when running my sentenceAnagrams function. Any Help !
type Word = String
type Sentence = List[Word]
type Occurrences = List[(Char, Int)]
def combinations(occurrences: Occurrences): List[Occurrences] = occurrences match {
case Nil => List(Nil)
case x :: xs => (for {z <- combinations(xs); i <- 1 to x._2} yield (x._1, i) :: z).union(combinations(xs))
}
def subtract(x: Occurrences, y: Occurrences): Occurrences = {
if (y.isEmpty) x
else {
val yMap = y.toMap withDefaultValue 0
x.foldLeft(x) { (z, i) => if (combinations(x).contains(y)) {
val diff = i._2 - yMap.apply(i._1)
if (diff > 0) z.toMap.updated(i._1, diff).toList else z.toMap.-(i._1).toList
} else z
}
}}
--
def sentenceAnagrams(sentence: Sentence): List[Sentence] = {
def sentenceAnag(occ: Occurrences): List[Sentence] =
if (occ.isEmpty) List(List())
else (for {
comb <- combinations(occ)
word <- (dictionaryByOccurrences withDefaultValue List()).apply(comb)
otherSentence <- sentenceAnag(subtract(occ, comb))
} yield word :: otherSentence).toList
sentenceAnag(sentenceOccurrences(sentence))
}

Update the values of a list with their absolute values

Newbie to scala.
I am trying to make this code to work for a few hours now . It is intended to update the List[Int](list of integers) with absolute values of the integers.
Took a long time to figure out that List is immutable, so found that ListBuffer can be the saviour, but eventually in returning it back into the List form is seeing some issue i guess.
def f (arr:List[Int]) : List[Int] =
{
val list = new scala.collection.mutable.ListBuffer[Int]();
val len = arr.length;
for ( i <- 0 to len)
{
if(arr(i) < 0)
{
list.append((-1)*arr(i)) ;
}
else
{
list.append(arr(i));
}
}
return list.toList;
}
which is giving this error:
java.lang.IndexOutOfBoundsException: 12
at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:52)
at scala.collection.immutable.List.apply(List.scala:84)
at Solution$.f(Solution.scala:7)
at Solution$delayedInit$body.apply(Solution.scala:23)
at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
at scala.App$$anonfun$main$1.apply(App.scala:71)
at scala.App$$anonfun$main$1.apply(App.scala:7...
Not getting what's wrong here.
The best way is to use Scala functions like #senia suggested in comments. For example:
val res = list map math.abs
But if you want to fix your code just replace to with until. You are getting off by one error:
def f (arr:List[Int]) : List[Int] =
{
val list = new scala.collection.mutable.ListBuffer[Int]();
val len = arr.length;
for ( i <- 0 until len)
{
if(arr(i) < 0)
{
list.append((-1)*arr(i)) ;
}
else
{
list.append(arr(i));
}
}
return list.toList;
}
Here is the difference between until and to:
1 to 3
// Range(1, 2, 3)
1 until 3
// Range(1, 2)
You can also remove return, ; and even braces { used with if/else.
Yet another version using a for comprehension that avoids indexing,
def f (arr:List[Int]) : List[Int] =
{
val list = new scala.collection.mutable.ListBuffer[Int]();
for {
a <- arr
sign = if (a < 0) -1 else 1
} list.append(sign * a)
return list.toList;
}
As mentioned above, the return may be omitted.
You can try using case statements for more neat syntax :
def f(arr:List[Int]):List[Int] = {
val list = scala.collection.mutable.ListBuffer[Int]()
arr.foreach{
x =>
x match {
case _ if (x <0) => list+= (x*(-1))
case _ => list +=x
}
}
list.toList
}
Looks like you were trying to solve the challenge from here. Probably you may want to use more functional approach with recursion and immutable List.
def f(arr: List[Int]): List[Int] = arr match {
case Nil => Nil
case x :: rest => java.lang.Math.abs(x) :: f(rest)
}
Beginner friendly: this is how I wrote it
def f(arr: List[Int]) : List[Int] = {
var list = new scala.collection.mutable.ArrayBuffer[Int]();
// var len = arr.length;
for(i <-0 until arr.length) {
list.append( math.abs(arr(i)));
}
return list.toList; }
I haven't done any time complexity analysis but it's the most straightforward for beginners to understand. Also, it passes all the tests on hackerrank
def f (arr: List[Int]) : List[Int] = {
arr.map {
case i if 0 > i => i * -1
case i => i
}
}

Scala: extracting a repeated value from a list

I have often the need to check if many values are equal and in case extract the common value. That is, I need a function that will work like follows:
extract(List()) // None
extract(List(1,2,3)) // None
extract(List(2,2,2)) // Some(2)
Assuming one has a pimp that will add tailOption to seqs (it is trivial to write one or there is one in scalaz), one implementation looks like
def extract[A](l: Seq[A]): Option[A] = {
def combine(s: A)(r: Seq[A]): Option[A] =
r.foldLeft(Some(s): Option[A]) { (acc, n) => acc flatMap { v =>
if (v == n) Some(v) else None
} }
for {
h <- l.headOption
t <- l.tailOption
res <- combine(h)(t)
} yield res
}
Is there something like that - possibly more general - already in Scalaz, or some simpler way to write it?
This seems like a really complicated way to write
def extract[A](l:Seq[A]):Option[A] = l.headOption.flatMap(h =>
if (l.tail.forall(h==)) Some(h) else None)
You don't need tailOption, since the anonymous function that gets passed as an argument to flatMap is only executed if l is not empty.
If you only want to delete duplicates toSet is enough:
def equalValue[A](xs: Seq[A]): Option[A] = {
val set = xs.toSet
if (set.size == 1) Some(set.head) else None
}
scala> equalValue(List())
res8: Option[Nothing] = None
scala> equalValue(List(1,2,3))
res9: Option[Int] = None
scala> equalValue(List(2,2,2))
res10: Option[Int] = Some(2)
This is a fluent solution
yourSeq.groupBy(x => x) match {case m if m.size==1 => m.head._1; case _ => None}
You could use a map to count the number of occurrences of each element in the list and then return only those that occur more than once:
def extract[T](ts: Iterable[T]): Iterable[T] = {
var counter: Map[T, Int] = Map()
ts.foreach{t =>
val cnt = counter.get(t).getOrElse(0) + 1
counter = counter.updated(t, cnt)
}
counter.filter(_._2 > 1).map(_._1)
}
println(extract(List())) // List()
println(extract(List(1,2,3))) // List()
println(extract(List(2,2,2))) // List(2)
println(extract(List(2,3,2,0,2,3))) // List(2,3)
You can also use a foldLeft instead of foreach and use the empty map as the initial accumulator of foldLeft.