Count of positives / sum of negatives Syntax Error - ruby

So I am currently working on the following problem in code wars:
Return an array, where the first element is the count of positive numbers and the second element is sum of negative numbers. If the input array is empty or null, return an empty array.
I came up with the following code, it ain't pretty, but I know it should work:
def count_positives_sum_negatives(lst)
pos, neg = 0, 0
lst.each do |num|
if num < 0
neg += num
else
pos++
end
end
[pos, neg]
end
I then call the following test:
count_positives_sum_negatives([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -11, -12, -13, -14, -15])
and it should return => [10,-65]
However its returning the following error:
(repl):11: syntax error, unexpected keyword_end
(repl):17: syntax error, unexpected end-of-input, expecting keyword_end
..., 10, -11, -12, -13, -14, -15])
...
Can anyone give me an explanation of why this is occurring?

As people have pointed out the error here is x++ is not valid Ruby, that you need x +=1 instead. The postfix and prefix increment operator is a point of wild confusion in most languages so Ruby has never implemented one. As in, what is the result of x++ + x++ + ++x?
Here's a strategy that's different using partition to first split the array, then collapse that together using inject:
positives, negatives = list.partition(&:positive?)
[ positives.length, negatives.inject(0, &:+) ]
# => [10,-65]

Here is another way you could do that.
def count_pos_sum_neg(arr)
return [] if arr.empty?
arr.each_with_object([0,0]) do |n,a|
a[0] += 1 if n > 0
a[1] += n if n < 0
end
end
count_pos_sum_neg [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -11, -12, -13, -14, -15]
#=> [10, -65]
In actual application, returning a hash may be more convenient.
def count_pos_sum_neg(arr)
return [] if arr.empty?
arr.each_with_object({count_pos: 0, sum_neg: 0}) do |n,h|
h[:count_pos] += 1 if n > 0
h[:sum_neg] += n if n < 0
end
end
count_pos_sum_neg [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -11, -12, -13, -14, -15]
#=> {:count_pos=>10, :sum_neg=>-65}

THe problem is pos++ which is not a valid operation in Ruby. It's being interpreted as an addition followed by a unary + but there's no number after the unary so it's expecting the next line to contain a value.
But the next line is end which is unexpected (hence the first error unexpected keyword_end) and because the end is now consumed you are missing an end for the method (hence the second error expecting keyword_end
So just change the line to...
pos += 1

Yet another variant using inject:
def count_pos_sum_neg(arr)
arr.inject([0, 0]) do |(count, sum), n|
if n > 0
[count + 1, sum]
else
[count, sum + n]
end
end
end
Or compressed:
def count_pos_sum_neg(arr)
arr.inject([0, 0]) { |(c, s), n| n > 0 ? [c + 1, s] : [c, s + n] }
end
The code doesn't check "If the input array is empty or null".

As I suspected your initial approach which is easy to read and simple, is actually fast too. Some results from a fruity comparison (code compacted):
require 'fruity'
arr = ((-1000..-1).to_a + (1..1000).to_a).shuffle
#various methods etc as defined in other answers.
compare do
wnamen { ar = arr; wnamen_method ar }
tadman { ar = arr; tadman_method ar }
cary { ar = arr; cary_method ar }
cary2 { ar = arr; cary2_method ar }
stefan { ar = arr; stefan_method ar }
stefan2 { ar = arr; stefan2_method ar }
end
results:
Running each test 8 times. Test will take about 1 second.
wnamen is faster than tadman by 2.0x ± 0.1
tadman is similar to stefan2
stefan2 is similar to stefan
stefan is similar to cary
cary is faster than cary2 by 19.999999999999996% ± 10.0% (results differ: [1000, -500500] vs {:count_pos=>1000, :sum_neg=>-500500})
All the other approaches are of course interesting and worth knowing nonetheless.

Related

Find the x amount of factorials starting from 0 using recursion. 5 == [1,1,2,6,24]

I am trying to get the first num factorials.
For example if num is 6 then we would want the first 6 factorials: 1,1,2,6,24,120
Below is my current solution. It always returns an extra factorial. So if num is 6 it returns 7 factorials.
def factorials_rec(num)
return [1] if num == 1
arr = factorials_rec(num-1)
arr << num * arr.last
end
Result [1, 1, 2, 6, 24, 120, 720]
Desired result [1, 1, 2, 6, 24, 120]
I only want 6 factorials but 7 are being returned.
Using recursion, how can I adjust this function to return the desired number of factorials.
I have tried
base case = return [1,1] if num == 2, etc..
arr << num * arr.last unless arr.length >= num, etc...
Try fixing the last line to:
arr << (num-1) * arr.last

Min, Max, Average, and Median of All Possible Sums (Ruby)

I've been using a piece of Ruby code that I found here.
Here's the code:
a = [1, 4, 7, 13]
def add(ary, idx, sum)
(idx...ary.length).each do |i|
add(ary, i+1, sum + ary[i])
end
puts sum
end
add(a, 0, 0)
Thing is, I don't need it to spit out the results of adding all the sums. I need the min, max, median, and average of the sums.
How do I modify this code in order to get them? I'm a total beginner at Ruby. I've been using this code, and then transferring the results to Excel to get the values I want. But it feels like my methods could be more efficient.
Thank you for your help.
EDIT: Expected results - Currently the code spits this out on my screen:
25
12
18
5
21
8
14
1
24
11
17
4
20
7
13
0
I want it to spit out the min, average, median, and max instead:
0
12.5
12.5
25
a = [1, 4, 7, 13]
def all_sums(array)
combination_lengths = (0..array.length)
all_combinations = combination_lengths.flat_map do |c|
array.combination(c).to_a
end
all_combinations.map(&:sum)
end
def print_min_max_avg_med(array)
puts array.min
puts array.max
puts array.sum.to_f / array.length
sorted_arr = array.sort
puts sorted_arr[(array.length - 1) / 2] + sorted_arr[array.length / 2] / 2.0
end
print_min_max_avg_med(all_sums(a))
Ok, instead of outputting the values we can store them in an arrary and use that array for the values you need.
(edited after chewing out by Stefan Pochmann)
a = [1, 4, 7, 13]
def add(ary, idx, sum, results = nil)
unless results
results = []
first_run = true
end
(idx...ary.length).each do |i|
add(ary, i+1, sum + ary[i], results)
end
results << sum
if first_run
puts results.min
puts results.inject(&:+).to_f / results.size
puts (results.sort[((results.size - 1) / 2)] + results.sort[(results.size / 2)]) / 2.0
puts results.max
end
end
add(a, 0, 0)
Alright, after seeing the examples from Pochmann and Bronca, I put this together after googling for a better way to get the median.
a = [1, 4, 7, 13]
def all_sums(array)
combination_lengths = (0..array.length)
all_combinations = combination_lengths.flat_map do |c|
array.combination(c).to_a
end
all_combinations.map(&:sum)
end
def median(array)
sorted = array.sort
len = sorted.length
(sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0
end
def print_min_max_avg_med(array)
puts array.min
puts array.empty? ? 0 : array.sum.to_f / array.length
puts median(array)
puts array.max
end
print_min_max_avg_med(all_sums(a))
I've run a few tests, and it seems to work for both odd and even arrays. Hope this is useful to the future somebody else stuck in my present position.
Thank you everyone who helped.
Min and Max
The min and max are easy.
def min_and_max_of_sums a
return [nil, nil] if a.empty?
negs, nonnegs = a.partition { |n| n < 0 }
[negs.any? ? negs.sum : nonnegs.min, nonnegs.any? ? nonnegs.sum : negs.max]
end
min_and_max_of_sums [1, 4, -5, 7, -8, 13]
#=> [-13, 25]
min_and_max_of_sums [1, 2, 3]
#=> [1, 6]
min_and_max_of_sums [-1, -2, -3]
#=> [-6, -1]
min_and_max_of_sums []
#=> [nil, nil]
Mean
Now consider the calculation of the mean.
If n is the size of the array a, there are 2n combinations of elements of a that contain between 0 and n elements.1 Moreover, there is a 1-1 mapping between each of those combinations and an n-vector of zeros and ones, where the ith element of the n-vector equals 1 if and only if the element ai is included in the combination. Note that there are 2n such n-vectors, one-half containing a 1 in the ith position. This means that one-half of the combinations contain the element ai. As i is arbitrary, it follows that each element of a appears in one-half of the combinations.
The mean of the sums of all elements of all combinations equals T/2n, where T is the sum of the sums of the elements of each combination. Each element ai appears in 2n/2 combinations, so its contribution to T equals (in Ruby terms)
a[i] * 2**(n)/2
As this hold for every element of a, the mean equals
a.sum * (2**(n)/2)/2**(n)
=> a.sum/2
Here's an example. For the array
a = [1, 4, 8]
the mean of the sums would be
a.sum/2
#=> 13/2 => 6.5
If we were to calculate the mean by its definition we would perform the following calculation (and of course get the same return value).
(0 + (1) + (4) + (8) + (1+4) + (1+8) + (4+8) + (1=4+8))/2**3
#=> (4*1 + 4*4 + 4*8)/8
#=> (1 + 4 + 8)/2
#=> 6.5
I will leave the calculating of the median to others.
1 Search for "Sums of the binomial coefficients" here.

(Ruby) First x Recursive Nums

I want to write a recursive method that returns the first num recursive numbers.
Here is my code so far:
def recursive_factorials(num)
return [1] if num == 1
arr = recursive_factorials(num-1)
arr << num * arr.last
end
Not sure what I'm doing wrong. The expected result for num = 6 is [1, 1, 2, 6, 24, 120], and I get [1, 2, 6, 24, 120, 720], so I may be close but really have no idea.
Any help would be appreciated. Also, if I am not using recursion properly please let me out.
Question is about recursion, but also you can use iteration, it's faster:
def factorials(num)
m = 1
(0...num).map {|e| e.zero? ? 1 : m *= e }
end
factorials(6)
=> [1, 1, 2, 6, 24, 120]
Or by using hash memoisation (I would say its a recursion too):
factorials = Hash.new { |h, k| h[k] = h[k-1] * k }.update(0 => 1)
factorials.values_at(*(0..5))
=> [1, 1, 2, 6, 24, 120]
Here is an example:
def recursive_factorials(num, acc = [])
acc << (num < 2 ? 1 : (num - 1) * recursive_factorials(num - 1, acc).last)
end
recursive_factorials 6
#⇒ [1, 1, 2, 6, 24, 120]
A variation of Ilya's answer:
def each_factorial
return enum_for(__method__) unless block_given?
m = 1
1.step do |i|
yield m
m *= i
end
end
each_factorial.take(6)
#=> [1, 1, 2, 6, 24, 120]

Check if numbers summed in array match input parameter

I'm following Ruby practice problem from a website and I'm completely stuck on figuring out a solution to this problem. Basically, given a function has_sum?(val, arr), return true if any combination of numbers in the array (second parameter) can be added together to equal the first parameter, otherwise return false. So:
has_sum?(5, [1, 2, 3, 4]) # true
has_sum?(5, [1, 2, 6]) # false
I'm completely stuck and not quite sure how to accomplish this... Here's what I have so far.
def has_sum?(val, arr)
arr.each_with_index do |idx, v|
# ??? no idea what to do here except add the current num to the next in the list
end
end
Any help would be greatly appreciated - thanks!
An array can produce a sum when there is a subset of any length that adds up to that sum:
def has_sum?(val, arr)
(arr.size + 1).times
.flat_map { |i| arr.combination(i).to_a }
.any? { |s| s.inject(:+) == val }
end
has_sum?(5, [5])
# => true
has_sum?(5, [1, 2, 3])
# => true
has_sum?(5, [1, 1, 1, 1, 1, 1])
# => true
has_sum?(5, [1, 2, 7])
# => false
This is not very efficient as it generates all the possibilities before testing. This should terminate sooner:
def has_sum?(val, arr)
(arr.size + 1).times.any? { |i|
arr.combination(i).any? { |s| s.inject(:+) == val }
}
end
Even more efficiently, a recursive implementation, with the idea that a sum of an empty array is zero (and has_sum(nonzero, []) should return false); for a larger array, we pop off its head, and see if the sum of the rest of the array is okay if we count, or don't count, the head element. Here, we don't do the useless summing of the whole array over and over again:
def has_sum?(val, arr)
if arr.empty?
val.zero?
else
first, *rest = arr
has_sum?(val, rest) || has_sum?(val - first, rest)
end
end
This solution employs dynamic programming. I assume that zeroes have been removed from the array. If all numbers in the array are positive, we can also remove elements that are larger than the target sum.
Code
def sum_to_target(arr, target)
h = arr.each_index.with_object({}) do |i,h|
v = arr[i]
h.keys.each do |n|
unless h.key?(n+v) # || (n+v > target)
h[n+v] = v
return reconstruct(h, target) if n+v == target
end
end
h[v] = v unless h.key?(v)
return reconstruct(h, target) if v == target
end
nil
end
def reconstruct(h, target)
a = []
loop do
i = h[target]
a.unshift i
target -= i
return a if target == 0
end
a
end
Additional efficiency improvements are possible if arr contains only postive values.1
Examples
#1
sum_to_target [2,4,7,2], 8
#=> [2, 4, 2]
#2
arr = [64, 18, 64, 6, 39, 51, 87, 62, 78, 62, 49, 86, 35, 57, 40, 15, 74, 10, 8, 7]
a = sum_to_target(arr, 461)
#=> [64, 18, 39, 51, 87, 62, 78, 62]
Let's check that.
a.reduce(:+)
#=> 461
#3
a = sum_to_target([-64, 18, 64, -6, 39, 51, -87, 62, -78, 62, 49, 86, 35, 57,
40, 15, -74, 10, -8, -7], 190)
#=> [18, 64, -6, 39, 51, -87, 62, 49]
a.reduce(:+)
#=> 190
#4
arr = 1_000.times.map { rand 1..5_000 }
#=> [3471, 1891, 4257, 2265, 832, 1060, 3961, 875, 614, 2308, 2240, 3286,
# ...
# 521, 1316, 1986, 4099, 1398, 3803, 4498, 4607, 2262, 3941, 4367]
arr is an array of 1,000 elements, each a random number between 1 and 5,000.
answer = arr.sample(500)
#=> [3469, 2957, 1542, 950, 4765, 3126, 3602, 755, 4132, 4281, 2374,
# ...
# 427, 4238, 4397, 2717, 912, 1690, 3626, 169, 3607, 4084, 3161]
answer is an array of 500 elements from arr, sampled without replacement.
target = answer.reduce(:+)
#=> 1_226_020
target is the sum of the elements of answer. We will now search arr for a collection of elements that sum to 1,226,020 (answer being one such collection).
require 'time'
t = Time.now
#=> 2016-12-12 23:00:51 -0800
a = sum_to_target(arr, target)
#=> [3471, 1891, 4257, 2265, 832, 1060, 3961, 875, 614, 2308, 2240, 3286,
# ...
# 3616, 4150, 3222, 3896, 631, 2806, 1932, 3244, 2430, 1443, 1452]
Notice that a != answer (which is not surprising).
a.reduce(:+)
#=> 1226020
(Time.now-t).to_i
#=> 60 seconds
For this last example, methods that use Array#combination would have to wade though as many as
(1..arr.size).reduce(0) { |t,i| t + arr.combination(i).size }.to_f
#~> 1.07+301
combinations.
Explanation
Let
arr = [2,4,7,2]
target = 8
Suppose we temporarily redefine reconstruct to return the hash passed to it.
def reconstruct(h, target)
h
end
We then obtain the following:
h = sum_to_target(arr, target)
#=> {2=>2, 6=>4, 4=>4, 9=>7, 13=>7, 11=>7, 7=>7, 8=>2}
h is defined as follows.
Given an array of non-zero integers arr and a number n, if n is a key of h there exists an array a containing elements from arr, in the same order, such that the elements of a sum to n and the last element of a equals h[n].
which, admittedly, is a mouthful.
We now use the reconstruct (as defined in the "Code" section) to construct an array answer that will contain elements from arr (without repeating elements) that sum to target.
reconstruct(h, target) #=> [2, 4, 2]
Initially, reconstruct initializes the array answer, which it will build and return:
answer = []
h will always contain a key equal to target (8). As h[8] #=> 2 we conclude that the last element of answer equals 2, so we execute
answer.unshift(2) #=> [2]
The problem is now to find an array of elements from arr that sum to 8 - 2 #=> 6. As h[6] #=> 4, we conclude that the element in answer that precedes the 2 we just added is 4:
answer.unshift(4) #=> [4, 2]
We now need 8-2-4 #=> 2 more to total target. As h[2] #=> 2 we execute
answer.unshift(2) #=> [2, 4, 2]
Since 8-2-4-2 #=> 0 we are finished and therefore return answer.
Notice that 4 precedes the last 2 in arr and the first 2 precedes the 4 in arr. The way h is constructed ensures the elements of answer will always be ordered in this way.
Now consider how h is constructed. First,
h = {}
As arr[0] #=> 2, we conclude that, using only the first element of arr, all we can conclude is:
h[2] = 2
h #=> {2=>2}
h has no key equal to target (8), so we continue. Now consider arr[1] #=> 4. Using only the first two elements of arr we can conclude the following:
h[2+4] = 4
h #=> {2=>2, 6=>4}
and since h has no key 4,
h[4] = 4
h #=> {2=>2, 6=>4, 4=>4}
h still has no key equal to target (8), so we press on and examine arr[2] #=> 7. Using only the first three elements of arr we conclude the following:
h[2+7] = 7
h[6+7] = 7
h[4+7] = 7
h #=> {2=>2, 6=>4, 4=>4, 9=>7, 13=>7, 11=>7}
and since h has no key 7:
h[7] = 7
h #=> {2=>2, 6=>4, 4=>4, 9=>7, 13=>7, 11=>7, 7=>7}
We added four elements to h, but since arr contains only positive numbers, those with keys 9, 13 and 11 are of no interest.
Since h still does not have a key equal to target (8), we examine the next element in arr: arr[3] #=> 2. Using only the first four elements of arr we conclude the following:
h[4+2] = 2
h[6+2] = 2
Here we stop, since 6+2 == target #=> true.
h #=> {2=>2, 6=>2, 4=>4, 9=>7, 13=>7, 11=>7, 7=>7, 8=>2}
Notice that we did not compute h[2+2] = 2 since h already has a key 4. Further, had arr contained additional elements we still would have terminated the construction of the hash at this point.
Had we modified the code to take advantage of the fact that arr contains only positive values, the final hash would have been:
h #=> {2=>2, 6=>2, 4=>4, 7=>7, 8=>2}
If this is still not clear, it might be helpful to run the code for this example with included puts statements (e.g., puts "i=#{i}, h=#{h}, v=#{v}" after the line v = arr[i] in sum_to_target, and so on).
1 The line unless h.key?(n+v) can be changed to unless h.key?(n+v) || (n+v > target) if it is known that the array contains no negative elements. (Doing so reduced the solution time for example #4 by 4 seconds.) One could also compute #all_positive = arr.all?(&:positive?) and then make that line conditional on #all_positive.
I would do nested loops.
for x = 0 to length of array
for y = x + 1 to length of array
if number at x + number at y = sum return true
return false
Basically it will check the sum of each number with each of the numbers after it.
EDIT: this will only sum 2 numbers at a time. If you want to be able to sum any amount of numbers this wont work.

Ruby, making a number negative

This is probably super basic, but I've tried enough things that have failed to reach out..
I want to change a number to it's negative version.
answer = []
array = [3, 5, 2, 19, 2, 1]
array.each.with_index do |x, i|
if x > array[i+1]
answer << array[i] * -1
else x =< array[i+1]
answer << array[i]
end
end
=> the answer I want is [-5] for when 'true' but I'm getting [5]
I also tried making a new 'negarray' with all the equivalent negative numbers as 'array'
answer = []
array = [3, 5, 2, 19, 2, 1]
negarray = [-3, -5, -2, -19, -2, -1]
=> again, getting [5], and not the [-5] I want.
Cheers!
In the actual version the questions is unclear.
If you mean with
I want to change a number to it's negative version.
that you want always a negative number, then you could try:
answer = []
array = [3, 5, 6, 19, 2, 1]
array.each do |x|
if x > 0
answer << x * -1
else
answer << x
end
end
p answer
or
array.each do |x|
answer << if x > 0
x * -1
else
x
end
end
or with a ternary operator:
array.each do |x|
answer << (x > 0 ? -x : x)
end
Or shorter and more ruby-esk (using a ternary operator):
array = [3, 5, 6, 19, 2, -1]
answer = array.map { |n| n > 0 ? -n : n }
If you prefer the longer if:
answer = array.map do |n|
if n > 0
-n
else
n
end
end
If you don't want to use any if-structure, then you could use a negative abs-method:
answer = array.map { |n| -n.abs }
WIth the following line
if x > array[i+1]
You are basically saying if the element at position i is greater than the position at i+1, you want to make it negative. The problem is that 5 is smaller than the next element 6 and for that reason it isn't being negated.
Let's fix up your code, and use the map method to simplify it:
out = array.map.with_index do |x, i|
(array[i+1].nil? || x > array[i+1]) ? x : x*-1
end
# [-3, -5, -6, 19, 2, 1]
If you want to get the negative value of the second array element at index 1, do the following
answer << array[1] * -1
In order to change ALL values of an array to negative numbers, use the following
answer = array.map { |n| -n }

Resources