CoffeeScript reduce skips values that are the same - coffeescript

I am using CoffeeScript to aggregate elements from a list into a combined object. However, when I have two values that are the same, one of the values gets left out. Instead of skipping one of these values, how can I get their sum?
metals = [
{ metal: 'silver', amount: 10 }
{ metal: 'gold', amount: 16 }
{ metal: 'iron', amount: 17 }
{ metal: 'iron', amount: 3 }
]
reduction = metals.reduce (x, y) ->
x[y.metal]= y.amount
x
, {}
console.log reduction
# => { silver: 10, gold: 16, iron: 3 }, but I would like to get iron: 20
here is a jsfiddle to help solve the problem https://jsfiddle.net/822trwez/

If you want reduce to sum things then you have to say so:
reduction = metals.reduce (x, y) ->
x[y.metal] = (x[y.metal] ? 0) + y.amount
x
, { }
The x[y.metal] ? 0 is just saying "if x[y.metal] is defined then use it, otherwise use 0". You could also say:
reduction = metals.reduce (x, y) ->
x[y.metal] = (x[y.metal] || 0) + y.amount
x
, { }
since you don't care about falsey values for x[y.metal] such as 0, '', false, null, or undefined; in your case you can convert all those to zero.
You could also be more explicit about what you're doing:
reduction = metals.reduce (x, y) ->
x[y.metal] = 0 if(y.metal !of x)
x[y.metal] += y.amount
x
, {}
The x[y.metal] = 0 if(y.metal !of x) just initializes x[y.metal] to zero if x doesn't have a y.metal property already. You could also use unless if you don't like !of:
reduction = metals.reduce (x, y) ->
x[y.metal] = 0 unless(y.metal of x)
x[y.metal] += y.amount
x
, {}
Keep in mind that all reduce does is runs the function you give it and feeds the function's output back to itself so:
[1,2,3].reduce f, i
is just:
f(f(f(i, 1), 2), 3)
What the function f does with its inputs and what it returns is up to you.

Related

Scala: Problem with foldLeft with negative numbers in list

I am writing a Scala function that returns the sum of even elements in a list, minus sum of odd elements in a list. I cannot use mutables, recursion or for/while loops for my solution. The code below passes 2/3 tests, but I can't seem to figure out why it can't compute the last test correctly.
def sumOfEvenMinusOdd(l: List[Int]) : Int = {
if (l.length == 0) return 0
val evens = l.filter(_%2==0)
val odds = l.filter(_%2==1)
val evenSum = evens.foldLeft(0)(_+_)
val oddSum = odds.foldLeft(0)(_+_)
evenSum-oddSum
}
//BEGIN TESTS
val i1 = sumOfEvenMinusOdd(List(1,3,5,4,5,2,1,0)) //answer: -9
val i2 = sumOfEvenMinusOdd(List(2,4,5,6,7,8,10)) //answer: 18
val i3 = sumOfEvenMinusOdd(List(109, 19, 12, 1, -5, -120, -15, 30,-33,-13, 12, 19, 3, 18, 1, -1)) //answer -133
My code is outputting this:
defined function sumOfEvenMinusOdd
i1: Int = -9
i2: Int = 18
i3: Int = -200
I am extremely confused why these negative numbers are tripping up the rest of my code. I saw a post explaining the order of operations with foldLeft foldRight, but even changing to foldRight still yields i3: Int = -200. Is there a detail I'm missing? Any guidance / help would be greatly appreciated.
The problem isn't foldLeft or foldRight, the problem is the way you filter out odd values:
val odds = l.filter(_ % 2 == 1)
Should be:
val odds = l.filter(_ % 2 != 0)
The predicate _ % 2 == 1 will only yield true for positive elements. For example, the expression -15 % 2 is equal to -1, and not 1.
As as side note, we can also make this a bit more efficient:
def sumOfEvenMinusOdd(l: List[Int]): Int = {
val (evenSum, oddSum) = l.foldLeft((0, 0)) {
case ((even, odd), element) =>
if (element % 2 == 0) (even + element, odd) else (even, odd + element)
}
evenSum - oddSum
}
Or even better by accumulating the difference only:
def sumOfEvenMinusOdd(l: List[Int]): Int = {
l.foldLeft(0) {
case (diff, element) =>
diff + element * (if (element % 2 == 0) 1 else -1)
}
}
The problem is on the filter condition that you apply on list to find odd numbers.
the odd condition that you doesn't work for negative odd number because mod 2 return -1 for this kind of number.
number % 2 == 0 if number is even
number % 2 != 0 if number is odd
so if you change the filter conditions all works as expected.
Another suggestion:
Why you want use foldleft function for a simple sum operation when you can use directly the sum functions?
test("Test sum Of even minus odd") {
def sumOfEvenMinusOdd(l: List[Int]) : Int = {
val evensSum = l.filter(_%2 == 0).sum
val oddsSum = l.filter(_%2 != 0).sum
evensSum-oddsSum
}
assert(sumOfEvenMinusOdd(List.empty[Int]) == 0)
assert(sumOfEvenMinusOdd(List(1,3,5,4,5,2,1,0)) == -9) //answer: -9
assert(sumOfEvenMinusOdd(List(2,4,5,6,7,8,10)) == 18) //answer: 18
assert(sumOfEvenMinusOdd(List(109, 19, 12, 1, -5, -120, -15, 30,-33,-13, 12, 19, 3, 18, 1, -1)) == -133)
}
With this solution your function is more clear and you can remove the if on the funciton

Combine multiple sequential entries in Scala/Spark

I have an array of numbers separated by comma as shown:
a:{108,109,110,112,114,115,116,118}
I need the output something like this:
a:{108-110, 112, 114-116, 118}
I am trying to group the continuous numbers with "-" in between.
For example, 108,109,110 are continuous numbers, so I get 108-110. 112 is separate entry; 114,115,116 again represents a sequence, so I get 114-116. 118 is separate and treated as such.
I am doing this in Spark. I wrote the following code:
import scala.collection.mutable.ArrayBuffer
def Sample(x:String):ArrayBuffer[String]={
val x1 = x.split(",")
var a:Int = 0
var present=""
var next:Int = 0
var yrTemp = ""
var yrAr= ArrayBuffer[String]()
var che:Int = 0
var storeV = ""
var p:Int = 0
var q:Int = 0
var count:Int = 1
while(a < x1.length)
{
yrTemp = x1(a)
if(x1.length == 1)
{
yrAr+=x1(a)
}
else
if(a < x1.length - 1)
{
present = x1(a)
if(che == 0)
{
storeV = present
}
p = x1(a).toInt
q = x1(a+1).toInt
if(p == q)
{
yrTemp = yrTemp
che = 1
}
else
if(p != q)
{
yrTemp = storeV + "-" + present
che = 0
yrAr+=yrTemp
}
}
else
if(a == x1.length-1)
{
present = x1(a)
yrTemp = present
che = 0
yrAr+=yrTemp
}
a = a+1
}
yrAr
}
val SampleUDF = udf(Sample(_:String))
I am getting the output as follows:
a:{108-108, 109-109, 110-110, 112, 114-114, 115-115, 116-116, 118}
I am not able to figure out where I am going wrong. Can you please help me in correcting this. TIA.
Here's another way:
def rangeToString(a: Int, b: Int) = if (a == b) s"$a" else s"$a-$b"
def reduce(xs: Seq[Int], min: Int, max: Int, ranges: Seq[String]): Seq[String] = xs match {
case y +: ys if (y - max <= 1) => reduce(ys, min, y, ranges)
case y +: ys => reduce(ys, y, y, ranges :+ rangeToString(min, max))
case Seq() => ranges :+ rangeToString(min, max)
}
def output(xs: Array[Int]) = reduce(xs, xs.head, xs.head, Vector())//.toArray
Which you can test:
println(output(Array(108,109,110,112,114,115,116,118)))
// Vector(108-110, 112, 114-116, 118)
Basically this is a tail recursive function - i.e. you take your "variables" as the input, then it calls itself with updated "variables" on each loop. So here xs is your array, min and max are integers used to keep track of the lowest and highest numbers so far, and ranges is the output sequence of Strings that gets added to when required.
The first pattern (y being the first element, and ys being the rest of the sequence - because that's how the +: extractor works) is matched if there's at least one element (ys can be an empty list) and it follows on from the previous maximum.
The second is if it doesn't follow on, and needs to reset the minimum and add the completed range to the output.
The third case is where we've got to the end of the input and just output the result, rather than calling the loop again.
Internet karma points to anyone who can work out how to eliminate the duplication of ranges :+ rangeToString(min, max)!
here is a solution :
def combineConsecutive(s: String): Seq[String] = {
val ints: List[Int] = s.split(',').map(_.toInt).toList.reverse
ints
.drop(1)
.foldLeft(List(List(ints.head)))((acc, e) => if ((acc.head.head - e) <= 1)
(e :: acc.head) :: acc.tail
else
List(e) :: acc)
.map(group => if (group.size > 1) group.min + "-" + group.max else group.head.toString)
}
val in = "108,109,110,112,114,115,116,118"
val result = combineConsecutive(in)
println(result) // List(108-110, 112, 114-116, 118)
}
This solution partly uses code from this question: Grouping list items by comparing them with their neighbors

scala foreach of a 2-D List/Array in chisel with types issue

Can "foreach" can be used for each element of the 2-D List/Array?
I tried the code:
val n_vec = (0 to 2).map(i=>
(0 to 2).map(j=>
Wire(UInt(3.W))
)
)
n_vec.foreach((i:Int)=>(
n_vec(i).foreach((j:Int)=>{
n_vec(i)(j) := i.U + j.U
})
))
the error message is
top.scala:24: error: type mismatch;
found : Int => Unit
required: chisel3.core.UInt => ?
n_vec(i).foreach((j:Int)=>{
^
Could you enlight me whether it can be used in such a way, even how?
It would be cleaner to write like this:
n_vec.foreach { i=>
i.foreach { j=>
j := x.U + y.U
y = y + 1
}
y = 0
x = x + 1
}
But you don't need to increment x and y manually, just iterate over indices instead:
n_vec.indices.foreach { x =>
n_vec(x).indices.foreach { y =>
n_vec(x)(y) := x.U + y.U
}
}
or better (and this translates exactly to the above)
for {
x <- n_vec.indices
y <- n_vec(x).indices
} {
n_vec(x)(y) := x.U + y.U
}
Yes it can be used this way.
solution:
var x = 0
var y = 0
n_vec.foreach(i=>{
i.foreach(j=>{
j := x.U + y.U
y = y + 1
})
y = 0
x = x + 1
})
x = 0
y = 0

Operations on array indices

I am new to app development and need help with some simple logic. I have an array of float values. The calculation involves two entries in the array but needs to be repeated until every possible combination of entries have been calculated.
ie x = (2* entry2) -entry1
Obviously my only way of referencing these entries is with index numbers. So I would assume x=(2*array[id+1])-array[id]
However my sequence would need to step the second array id down, and once 0 is reached increment both array IDs +1 and repeat the process until the first ID has reached the maximum ID number.
Or am I going about this the wrong way. Any help would be appreciated.
Currently I have RFArray and I want to populate IMProdArray so my code would be:
IMProdArray+=[2*(RFArray[2])-(RFArray[1])]
But I need a loop to repeat this for all possible combinations of ID numbers in the array.
So If I had 4 entries in the Array:
IMProdArray+=[2*(RFArray[3])-(RFArray[2])]
IMProdArray+=[2*(RFArray[3])-(RFArray[1])]
IMProdArray+=[2*(RFArray[3])-(RFArray[0])]
IMProdArray+=[2*(RFArray[2])-(RFArray[1])]
IMProdArray+=[2*(RFArray[2])-(RFArray[0])]
IMProdArray+=[2*(RFArray[1])-(RFArray[0])]
And then the reverse the order for all ID numbers:
IMProdArray+=[2*(RFArray[2])-(RFArray[3])]
IMProdArray+=[2*(RFArray[1])-(RFArray[3])]
IMProdArray+=[2*(RFArray[0])-(RFArray[3])]
IMProdArray+=[2*(RFArray[1])-(RFArray[2])]
IMProdArray+=[2*(RFArray[0])-(RFArray[2])]
IMProdArray+=[2*(RFArray[0])-(RFArray[1])]
Thanks,
SamP
For example, define an Array extension with a function that returns all the pairwise combinations of its elements, then map over the pairs with the calculation you wish to perform:
extension Array {
func pairs() -> [(T, T)] {
// you can take this nested func out for reuse
func reverseIndexPairs(var count n: Int) -> [(Int, Int)] {
assert(n >= 0)
var ps = [(Int, Int)]()
for e1 in stride(from: n-1, through: 0, by: -1) {
n--
for e2 in stride(from: n-1, through: 0, by: -1) {
ps.append((e1, e2))
}
}
return ps
}
return reverseIndexPairs(count: self.count).map {
(self[$0.0], self[$0.1])
}
}
}
let arr: [Double] = [0,1,2,3]
println("test order: ")
for e in [0,1,2,3].pairs() {
println(e)
}
println("\nresult array <- 2 * x - y:")
let resultArray = arr.pairs().map { x, y in 2 * x - y }
for e in resultArray {
println(e)
}
println("\nresult array <- 2 * y - x:")
let resultReverse = arr.pairs().map { x, y in 2 * y - x }
for e in resultReverse {
println(e)
}
If I understood you right, you want the pairs to be in the reverse order, so the code above prints:
test order:
(3, 2)
(3, 1)
(3, 0)
(2, 1)
(2, 0)
(1, 0)
result array <- 2 * x - y:
4.0
5.0
6.0
3.0
4.0
2.0
result array <- 2 * y - x:
1.0
-1.0
-3.0
0.0
-2.0
-1.0

Recursive function is overflowing, why?

The below function is overflowing, and I don't understand why. When run with x as 0, y as 0 and dim as 2, the result should be 6. However, I am getting an error indicating that some Long value in the function (either x or y) is 554 at the time of overflow. This should not be possible as x and y are both bounded by the dim value, which in my test is set to 2.
Here is the code:
def lattice(dim: Long, x: Long, y: Long): Long = {
if (x == dim && y == dim) {
1
}
if (x >= dim) {
lattice(dim,x,y+1L)
}
if (y >= dim) {
lattice(dim,x+1L,y)
}
else {
lattice(dim,x+1L,y) + lattice(dim,x,y+1L)
}
}
You are missing else in two places. This means that your final line runs even when x >= dim, causing x to exceed dim. Try this instead:
if (x == dim && y == dim) {
1
} else if (x >= dim) {
lattice(dim,x,y+1L)
} else if (y >= dim) {
lattice(dim,x+1L,y)
} else {
lattice(dim,x+1L,y) + lattice(dim,x,y+1L)
}
Chris:
Youd function is not tail recursive because in your last statement you have a sum. By chance this sum involves two calls to lattice. However, to be tail recursive your last statament must be a constant (like you did in your first if) or just a call to function itself (such as your 2 else ifs.
I admit that I don't know how to change your function to be tail recursive. Depending of your alghoritm, perhaps it is not possible to do your function tail recursive.
Thanks,
Rafael Afonso