Related
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.
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 }
I'm writing a method - prime_numbers - that, when passed a number n, returns an n number of primes. It should not rely on Ruby's Prime class. It should behave like so:
prime_numbers 3
=> [2, 3, 5]
prime_numbers 5
=> [2, 3, 5, 7, 11]
My first attempt at this method is as follows:
def prime_numbers(n)
primes = []
i = 2
while primes.length < n do
divisors = (2..9).to_a.select { |x| x != i }
primes << i if divisors.all? { |x| i % x != 0 }
i += 1
end
primes
end
Edit: As pointed out, the current method is at fault by being limited to take into account divisors only up to 9. As a result, any perfect square composed of two equal primes greater than 9 is treated as a prime itself.
If anyone has input or tips they can share on better ways to approach this, it would be greatly appreciated.
Note that if the number is composite it must have a divisor less than or equal to $\sqrt{n}$. So you really only have to check up to $sqrt{n}$ to find a divisor.
Got a good idea for your implementation:
#primes = []
def prime_numbers(n)
i = 2
while #primes.size < n do
#primes << i if is_prime?(i)
i += 1
end
#primes
end
def is_prime?(n)
#primes.each { |prime| return false if n % prime == 0 }
true
end
This is based on the idea that non-prime numbers have prime factors :)
In Ruby 1.9 there is a Prime class you can use to generate prime numbers, or to test if a number is prime:
require 'prime'
Prime.take(10) #=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Prime.take_while {|p| p < 10 } #=> [2, 3, 5, 7]
Prime.prime?(19) #=> true
Prime implements the each method and includes the Enumerable module, so you can do all sorts of fun stuff like filtering, mapping, and so on.
I need to check whether the sum of any 2 elements of an array equals to the given number. This is what I came up with, but it doesn't seem to do the comparison
def sum_comparison(int_array, x)
n = int_array.length
(0..n).each do |i|
(1..n).each do |j|
if ((int_array[i].to_i + int_array[j].to_i) == x)
return true
else
return false
end
end
end
end
Your solution seems overly complicated and strongly influenced by the programming style of low-level procedural languages like C. One apparent problem is that you write
n = int_array.length
(0..n).each do |i|
# use int_array[i].to_i inside the loop
end
Now inside the each loop, you will get the numbers i = 0, 1, 2, ..., n, for example for int_array = [3,4,5] you get i = 0, 1, 2, 3. Notice that there are four elements, because you started counting at zero (this is called an off by one error). This will eventually lead to an array access at n, which is one beyond the end of the array. This will again result in a nil coming back, which is probably why you use to_i to convert that back to an integer, because otherwise you would get a TypeError: nil can't be coerced into Fixnum whend doing the addition. What you probably wanted instead was simply:
int_array.each do |i|
# use i inside the loop
end
For the example array [3,4,5] this would actually result in i = 3, 4, 5. To get the combinations of an array in a more Ruby way, you can for example use Array#combination. Likewise, you can use Array#any? to detect if any of the combinations satisfy the specified condition:
def sum_comparison(array, x)
array.combination(2).any? do |a, b|
a + b == x
end
end
When your function compare first element, it's immediately returns false. You need to return only true when iterating and return false at the end if nothing were found, to avoid this issue:
def sum_comparison(int_array, x)
n = int_array.size
(0...n).each do |i|
(1...n).each do |j|
if (int_array[i].to_i + int_array[j].to_i) == x
return true
end
end
end
false
end
To simplify this you can use permutation or combination and any? methods as #p11y suggests. To get founded elements you could use find or detect.
def sum_comparison(a, x)
a.combination(2).any? { |i, j| i + j == x }
end
a.combination(2).detect { |i, j| i + j == x }
# sum_comparison([1,2,3, 4], 6) => [2, 4]
Using an enumerator:
#!/usr/bin/env ruby
def sum_comparison(int_array, x)
enum = int_array.to_enum
loop do
n = enum.next
enum.peek_values.each do |m|
return true if (n + m) == x
end
end
false
end
puts sum_comparison([1, 2, 3, 4], 5)
Output:
true
Problem
Your method is equivalent to:
def sum_comparison(int_array, x)
return int_array[0].to_i + int_array[1].to_i == x
end
Therefore,
int_array = [1,2,4,16,32,7,5,7,8,22,28]
sum_comparison(int_array, 3) #=> true, just lucky!
sum_comparison(int_array, 6) #=> false, wrong!
Alternative
Here is a relatively efficient implemention, certainly far more efficient than using Enumerable#combination.
Code
def sum_comparison(int_array, x)
sorted = int_array.sort
smallest = sorted.first
sorted_stub = sorted.take_while { |e| e+smallest <= x }
p "sorted_stub = #{sorted_stub}"
return false if sorted_stub.size < 2
loop do
return false if sorted_stub.size < 2
v = sorted_stub.shift
found = sorted_stub.find { |e| v+e >= x }
return true if found && v+found == x
end
false
end
Examples
sum_comparison([7,16,4,12,-2,5,8], 3)
# "sorted_stub = [-2, 4, 5]"
#=> true
sum_comparison([7,16,4,12,-2,5,8], 7)
# "sorted_stub = [-2, 4, 5, 7, 8]"
#=> false
sum_comparison([7,16,4,22,18,12,2,41,5,8,17,31], 9)
# "sorted_stub = [2, 4, 5, 7]"
#=> true
Notes
The line p "sorted_stub = #{sorted_stub}" is included merely to display the array sorted_stub in the examples.
If e+smallest > x for any elements f and g in sorted for which g >= e and f < g, f+g >= e+smallest > x. Ergo, sorted_stub.last is the largest value in sorted that need be considered.
For a given value v, the line found = sorted_stub.find { |e| v+e >= x } stops the search for a second value e for which v+e = x as soon as it finds e such that v+e >= x. The next line then determines if a match has been found.
I've been going at this problem for a few hours, and I can't see why I can't get it to run properly. The end game to this method is having 2 numbers in an array equaling zero when added together. Here is my code:
def two_sums(nums)
i = 0
j = -1
while i < nums.count
num_1 = nums[i]
while j < nums.count
num_2 = nums[j]
if num_1 + num_2 == 0
return "There are 2 numbers that sum to zero & they are #{num_1} and #{num_2}."
else
return "Nothing adds to zero."
end
end
i += 1
j -= 1
end
end
The problem I'm having is unless the first and last number in the array are the positive and negative of the same number, this will always return false.
For example, if I had an array that was [1, 4, 6, -1, 10], it should come back true. I'm sure my 2 while statement is the cause of this, but I can't think of a way to fix it. If someone could point me in the right direction, that would be helpful.
You can find the first pair that adds up to 0 like this:
nums.combination(2).find { |x, y| x + y == 0 }
#=> returns the first matching pair or nil
Or if you want to select all pairs that add up to 0:
nums.combination(2).select { |x, y| x + y == 0 }
#=> returns all matching pairs or an empty array
Therefore you can implement your method like this:
def two_sums(nums)
pair = nums.combination(2).find { |x, y| x + y == 0 }
if pair
"There are 2 numbers that sum to zero & they are #{pair.first} and #{pair.last}."
else
"Nothing adds to zero."
end
end
Or if you want to find all pairs:
def two_sums(nums)
pairs = nums.combination(2).select { |x, y| x + y == 0 }
if pairs.empty?
"Nothing adds to zero."
else
"The following pairs sum to zero: #{pairs}..."
end
end
Here's another way:
Code
def sum_to_zero(arr)
arr.group_by { |e| e.abs }
.values
.select { |a| (a.size > 1 && a.first == 0) || a.uniq.size > 1 }
end
Examples
sum_to_zero [1, 4, 6, -1, 10] #=> [[1, -1]]
sum_to_zero [1, 4, 1, -2, 10] #=> []
sum_to_zero [1, 0, 4, 1, 0, -1] #=> [[1, 1, -1], [0, 0]]
This method is relatively fast. Let's try it with an array of 200,000 elements, each a random number between -500,000 and 500,000.
require 'time'
t = Time.now
arr = Array.new(200_000) { rand(1_000_001) - 500_000 }
arr.size #=> 200000
sum_to_zero(arr).size #=> 16439
Time.now - t
#=> 0.23 (seconds)
sum_to_zero(arr).first(6)
#=> [[-98747, 98747],
# [157848, -157848],
# [-459650, 459650],
# [176655, 176655, -176655],
# [282101, -282101],
# [100886, 100886, -100886]]
If you wish to group the non-negative and negative values that sum to zero:
sum_to_zero(arr).map { |a| a.partition { |e| e >= 0 } }.first(6)
#=> [[[98747], [-98747]],
# [[157848], [-157848]],
# [[459650], [-459650]],
# [[176655, 176655], [-176655]],
# [[282101], [-282101]],
# [[100886, 100886], [-100886]]]
If you only want a single value for each group (a non-negative value, say):
sum_to_zero(arr).map { |a| a.first.abs }.first(6)
#=> [98747, 157848, 459650, 176655, 282101, 100886]
I think the most Ruby way would be:
nums.combination(2).any? { |x,y| (x+y).zero? }
Here's a way that should work well for large arrays. The methods above which go through every possible combination of two numbers are perfectly fine for small cases but will be very slow and memory hungry for arrays with lots of elements.
def two_sums nums
h = Hash.new
nums.each do |n|
return true if h[-n]
h[n] = true
end
false
end
Well, given it's tagged as #ruby, here's the most "ruby way" I could think of tackling this problem:
def two_sums(arr)
numbers = arr.combination(2).select { |a| a.reduce(:+) == 0 }.flatten
if numbers.empty?
"Nothing adds to zero."
else
"There are 2 numbers that sum to zero & they are #{numbers.first} and #{numbers.last}."
end
end
array.combination(2).select{|x|x[0] + x[1] == 0}