Exclusions in map in Ruby - ruby

How do I create an exclusion for a array map in Ruby.
Here's what I want to achieve,
a = [1,2,3,4]
b = [5,6,7,8]
a.map.each do |x|
b.map.each do |y|
if !(x == 1 && y == 7)
puts "#{x} and #{y}"
elsif !(x == 4 && y == 8)
puts "#{x} and #{y}"
end
end
end
1 and 5
1 and 6
1 and 7 # still here
1 and 8
2 and 5
2 and 6
2 and 7
2 and 8
3 and 5
3 and 6
3 and 7
3 and 8
4 and 5
4 and 6
4 and 7
4 and 8 # still here
However, it doesn't work, how do I add an exception to these values being processed by map? Also if it's possible to use inject/reject/filter function with the same goal.

To explain why 1 and 7 is still printing, step through the logic:
if !(x == 1 && y == 7)
x == 1 is true and y == 7 is true, therefore !(true && true) is false, this is skipped.
elsif !(x == 4 && y == 8)
the if was skipped, so the elsif is evaluated. x == 4 is false (since x is still 1) and y == 8 is false (since y is still 7). Therefore, !(false && false) is true, and the puts is reached.
Because x can never be both 1 and 4 at the same time and y can never be 7 and 8 at the same time, either your if statement or your elsif statement will always succeed, and since both branches print, the values will be always printed, no matter what x and y are.
As other answers said, you need to combine your clauses.

This is just a problem of not understanding disjunctive semantics in an if statement.
If you want a value NOT to be printed at all, it must match ALL of the negative conditions. Since your predicate is the same (using puts), all you need to do is combine the if statements with an "and" keyword.
That is, something like:
if !(x == 1 && y == 7) and !(x == 4 && y == 8)

I think this does what you want:
a = [1,2,3,4]
b = [5,6,7,8]
a.map.each do |x|
b.map.each do |y|
if !(x == 1 && y == 7) && !(x == 4 && y == 8)
puts "#{x} and #{y}"
end
end
end
(tested on codepad)
Your old code only tested that !(x == 1 && y == 7) was true OR that !(x == 4 && y == 8)
was true - it did not test them both. So when x was 1 and y was 7, the first puts did not execute, but the second one did. Execute this code to trace it better:
a = [1,2,3,4]
b = [5,6,7,8]
a.map.each do |x|
b.map.each do |y|
if !(x == 1 && y == 7)
puts "First #{x} and #{y}"
elsif !(x == 4 && y == 8)
puts "SEcond #{x} and #{y}"
end
end
end

Another variation using a patched Array class and the #reject method.
class Array
def * arr
outer = []
self.each do |a|
arr.each do |b|
outer << [a,b]
end
end
outer
end
end
a = [1,2,3,4]
b = [5,6,7,8]
c = (a * b).reject {|pair| pair == [1,7] || pair == [4,8]}
c.each {|pair| puts "#{pair[0]} and #{pair[1]}"}
Not sure about using the '*' operator, it would need a better implementation if you wanted to keep the ability to multiply arrays by a Fixnum, but i like the syntax for applying methods to a combination of the two arrays which is a fairly common requirement.

I think it would be a good idea to seperate out the seperate steps:
compute the Cartesian Product of the two arrays: a.product(b)
filter out the unwanted pairs: reject {|x, y| [[1, 7], [4, 8]].any? {|pair| [x, y] == pair }
}
convert to string: map {|pair| pair.join(' and ') }
print it: puts
That's what we end up with:
puts a.product(b).reject {|x, y|
[[1, 7], [4, 8]].any? {|pair| [x, y] == pair }
}.map {|pair| pair.join(' and ') }

Related

Ruby Prime Factors

I've seen solutions posted in other languages but not Ruby so I'm asking here.
Trying to find out the largest prime factor of 13195.
My code is as follows
# find out all numbers that divide without remainder into 13195
array = []
count = 2
13195.times do
if 13195 % count == 0
array.push(count)
end
count += 1
end
#From those numbers that divide cleanly into 13195, find out which are prime aka can only be divided by themselves and 1
new_count = 2
primes = 0
array.each do |x|
while new_count < x
if x % new_count != 0
else
if x > primes
primes = x
end
end
new_count += 1
end
end
puts primes
In my first loop I am populating an empty array with all the numbers that divide into 13195 without a remainder, from testing this piece of code seems to be working.
It's the second part of my solution that's the problem inside my each statement, can someone point me in the right direction?
I suggest you use Prime#prime_division:
require 'prime'
def largest_prime_factor(n)
Prime.prime_division(n).max_by(&:first).first
end
largest_prime_factor(13195)
#=> 29
(1..1000).to_a.sample(15).sort.each {|n| puts "%3d: %3d" % [n, largest_prime_factor(n)]}
61: 61
80: 5
88: 11
250: 5
304: 19
414: 23
514: 257
548: 137
679: 97
716: 179
754: 29
770: 11
906: 151
907: 907
968: 11
For example,
n = 13195
a = Prime.prime_division(n)
#=> [[5, 1], [7, 1], [13, 1], [29, 1]]
b = a.max_by(&:first)
#=> [29, 1]
b.first
#=> 29
It appears that the elements of the array returned by prime_division are in order of increasing prime factor. If that were guaranteed, one could just write:
Prime.prime_division(n).last.first
I used max_by in the event that the order of those elements is implementation-specific.
Shorter version:
require 'prime'
primes = Prime.each(13195).to_a
upper = primes.last
primes will have all primes from 0 to 13195 and upper obviously the last.
I set a limit of prime numbers to 100000 (to avoid of couple days of calculation of big numbers like 600851475143 =) )
def prime_factors(n)
prime_array = []
p = 2
if n < 2
return p
end
while p < n && p < 1000000
if n % p == 0
prime_array.push(p)
end
p +=1
end
primes = []
prime_array.size.times do |i|
if n > 1
n = n / prime_array[i]
primes.push(prime_array[i])
end
end
return primes.last
end
#prime_factors(600851475143)
puts prime_factors(600851475143)
#prime_factors(13195)
puts prime_factors(13195)
Another way to use prime_division:
require 'prime'
(13195).prime_division.map(&:first).max
=> 29
Your second loop can be re-written to do what is meant to do.
As I understand, your goal is to select from array, largest of such elements that are prime (divides by only 1 and itself). In other words, an element x is eligible if it is not divisible by any number between 2 and x-1.
result = array.select {|x| not (2..x-1).any? {|i| x % i == 0} }.max
#=> 29
Currently, your logic has some flaws. It is not resetting the value of new_count and hence you are getting wrong results. Here is corrected version:
array.each do |x|
is_prime = true
while new_count < x
if x % new_count == 0
is_prime = false
end
new_count += 1
end
new_count = 2
primes = x if is_prime and x > primes
end
Without using Prime#prime_division we can dived the problems into small parts. the first part if to write a method that determine whether a number if prime or not, which can be like :
def prime?(num)
if num < 2
return false
end
(2...num).each do |ele|
if num % ele == 0
return false
end
end
return true
end
then we can write our main method which is asking for the prime_factors of a number like:
def prime_factors(num)
prime_facts = []
(1..num).each do |i|
if num % i == 0 && prime?(i)
prime_facts << i
end
end
return prime_facts
end
print prime_factors(24) #=> [2, 3]
puts
print prime_factors(60) #=> [2, 3, 5]
puts
print prime_factors(13195) # [5, 7, 13, 29]
puts

Find multiples of 3 or 5 below 10

I am trying to find the sum of all multiples of 3 or 5 below 10
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
result = 0
numbers.each do |number|
if number % 3 == 0 or number % 5 == 0
result += number
end
print result
end
I receive this: 0033814141423, but I expect 23, as it's the sum of 3, 5, 6, 9.
Your print statement is inside the loop. It should be moved outside.
numbers = [1,2,3,4,5,6,7,8,9]
result = 0
numbers.each do |number|
if number % 3 == 0 or number % 5 == 0
result += number
end
end
print result
A proper indentation will also help you to catch these errors. Moreover, you should use p to print out an information, not print.
With p each output would have been done on a new line. That would probably be an hint to understand the issue.
This is also a possible alternative using Enumerable#inject with an accumulator.
numbers = [1,2,3,4,5,6,7,8,9]
numbers.inject(0) do |acc, number|
acc + case
when number % 3 == 0 then number
when number % 5 == 0 then number
else 0
end
end
And a more compact form
numbers = [1,2,3,4,5,6,7,8,9]
numbers.lazy.select { |n| n % 3 == 0 || n % 5 == 0 }.inject(:+)
You may use Array#select method.
result = 0
[1,2,3,4,5,6,7,8,9].select{|i| i % 3 == 0 || i % 5 == 0}.each do |num|
result += num
end
or more shortly with inject.
[1,2,3,4,5,6,7,8,9].select{|i| i % 3 == 0 || i % 5 == 0}.inject(:+)
Well, what I did was something like this:
(1...10).to_a.select{|i| i%3==0 or i%5==0}.sum

Compare sums of elements in an array: Ruby

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.

Checking to see if 2 numbers in array sum to 0 in Ruby

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}

What is the proper syntax for multiple comparisons?

Is there a proper syntax in Ruby for comparing multiple values against the same variable? For example:
#!/usr/bin/ruby -w
y = 15
p 'success' if y == 1 || y == 5 || y == -2 || y == 15132 || y == 3.14159265 || y == 15
Can that be written as something along the lines of:
y = 15
p 'success' if y == 1,5,-2,15132,3.14159265,15
And, if so, would that also apply to while loops?
y = 15
while y != 1,5,-2,15132,3.14159265,15
y = rand(50)
p y
end
Based on my search I'm tending to believe this is either not possible, or it's too obscure for my searches.
I hope it's the second case.
I have already considered an array itteration solution, but it's not as pretty or simple as I'd like.
You're looking for include?
p 'success' if [1,5,-2,15132,3.14159265,15].include? y
p 'success' if [1, 5, -2, 15132, 3.14159265, 15].include? y
case y
when 1, 5, -2, 15132, 3.14159265, 15 then p "success"
end
For a more general case you can use the any? method with a comparison block; this has the advantage of being usable with operators apart from ==:
p 'success' if [1, 5, -2, 15132, 3.14159265, 15].any? { |i| i == y }
From the Array#index :
Returns the index of the first object in ary such that the object is == to obj.Returns nil if no match is found.
p 'success' if [1,5,-2,15132,3.14159265,15].index(y)

Resources