I know that I can do:
(1..30).cover?(2)
=> true
But when I try to do the same with another range it always returns false:
(1..30).cover?(2..3)
=> false
So my question is - is there any elegant way to compare two ranges in ruby? In my case I want to check if two DateTime-ranges overlap. Thanks in advance.
Two ranges overlap for a given range A when:
range B starts within range A,
range B ends within range A or
range B starts before range A and ends after range A
Examples:
Range A |-----|
|-----| Case 1
|-----| Case 2
|-| Case 1 + 2
|---------| Case 3
Looking closer the rule is: Two ranges overlap when Range B starts before the range A ends and range B ends after the range A starts.
def ranges_overlap?(range_a, range_b)
range_b.begin <= range_a.end && range_a.begin <= range_b.end
end
def overlap?(r1,r2)
!(r1.first > r2.last || r1.last < r2.first)
end
overlap? 1..5, 4..10 #=> true
overlap? 1..5, 6..10 #=> false
overlap? 1..10, 4..8 #=> true
overlap? 1..4, 4..8 #=> true
The operative line is equivalent to:
r1.first <= r2.last && r1.last >= r2.first
I normally try to avoid negation, but in this case I think it reads better with it.
Another way:
def overlap?(r1,r2)
!(([r1.first, r2.first].min..[r1.last, r2.last].max).size >= r1.size + r2.size)
end
overlap? 1..5, 4..10 #=> true
overlap? 1..5, 6..10 #=> false
overlap? 1..10, 4..8 #=> true
overlap? 1..4, 4..8 #=> true
The operative line is equivalent to:
([r1.first, r2.first].min..[r1.last, r2.last].max).size < r1.size + r2.size
Again, I prefer the one with negation.
In rails You can use (1..3).overlaps?(2..4) # true
https://apidock.com/rails/Range/overlaps
If you want the rails implementation you can use
class Range
def overlaps?(other)
cover?(other.first) || other.cover?(first)
end
end
(1..5).overlaps?(4..6) # => true
(1..5).overlaps?(7..9) # => false
While the conversions may be wasteful, semantically comparing sets seems to make the most sense:
Set.new(1..30).superset?(Set.new(2..3))
#=> true
Set.new(1..30).superset?(Set.new(0..3))
#=> false
If you don't want to do that, you can do something like this (with r1 and r2 being ranges):
r1.cover?(r2.min) && r1.cover?(r2.max)
You can check Overlapping using
range1.first < range2.last && range2.first < range1.last
You can add it as an instance method of range or as a helper method somewhere in you data.
source: https://stackoverflow.com/a/325964/4091324
Related
Create a function that takes an array of hurdle heights and a jumper's jump height, and determine whether or not the hurdler can clear all the hurdles. A hurdler can clear a hurdle if their jump height is greater than or equal to the hurdle height.
My code:
def hj (arr, h)
i = 0
while i < arr.length
j = 0
while j < arr.length
if arr[i] > h
return false
end
j += 1
end
return true
i += 1
end
end
puts hj([2, 3, 6, 1, 3, 1, 8], 7)
Desired output: true if h is >= to any number in the array; false if h is < any number in the array (I want true or false to display once)
Where I'm questioning my own code:
Not sure if I need two while statements
the current array being passed should output false
loop seems to only be comparing the first set of numbers, so 7 and 2. Not sure why the loop is stopping.
Not sure if I'm utilizing true and false correctly
Feel like I should be using a block for this, but not sure where to implement it.
Thank you in advance for any feedback.
Some solutions:
Using loop
def hj(arr, h)
for elem in arr
return false if elem > h
end
true
end
See? Only one loop. Actually this is the most unruby implementation.
Using Enumerable#all?
def hj(arr, h)
arr.all?{|elem| elem <= h}
end
This is the most intuitive and most Ruby implementation.
Using Enumerable#max
If one can jump over the highest hurdle, he can jump over all hurdles.
def hj(arr, h)
arr.max <= h
end
Not sure if I need two while statements
You don't. You only need to traverse the list once. You're traversing, not sorting / reordering.
loop seems to only be comparing the first set of numbers, so 7 and 2. Not sure why the loop is stopping.
This is because you are using return true as the second last statement of your outer loop. Return interrupts function execution and returns immediately to the calling function - in this case, the last line of your program.
Feel like I should be using a block for this, but not sure where to implement it.
A block is the idiomatic ruby way to solve this. You are, essentially, wanting to check that your second parameter is larger than any value in the list which is your first parameter.
A solution in idiomatic ruby would be
def hj (arr, h)
# return true if h >= all elements in arr, false otherwise
# given arr = [1, 2, 3] and h = 2,
# returns [ true, true, false ] which all? then returns as false
# (since all? returns the boolean AND of the results of the block evaluation
arr.all? { |elem| elem <= h }
end
I am attempting to code hitboxes within Gosu in ruby, and want to check if 2 ranges meet (the ranges being co-ords) i want it to simply give true or false
I've looked into it and found the range.cover? code, however after testing this shows it only checks if one range entirely fits inside another, and not if they only partially connect.
#both sprites are arrays, with the following structure
#[image_data, sprite_x, sprite_y]
#image_data.width would return how wide the image is
#The x and y is the top left of the sprite
def hit_scan(sprite1, sprite2)
x_connect = (sprite1[1]..sprite1[1] + sprite1[0].width).cover?(sprite2[1]..(sprite2[1] + sprite2[0].width))
y_connect = (sprite1[2]..sprite1[2] + sprite1[0].height).cover?(sprite2[2]..(sprite2[2] + sprite2[0].height)
if x_connect == true
if y_connect == true
return true
else
return false
end
else
return false
end
end
This is what I tried, and only returns true when the entire sprite is in the other one.
I expected that whenever the sprites touched, it would return a true statement, but only when one sprite is in another does it return true.
You can check if one range includes begin or end of another one:
r1 = (1..5)
r2 = (4..8)
r1.include?(r2.begin) || r1.include?(r2.end) || r2.include?(r1.begin) || r2.include?(r1.end)
In your case:
r1 = (sprite1[1]..sprite1[1] + sprite1[0].width)
r2 = (sprite2[1]..sprite2[1] + sprite2[0].width)
r1.include?(sprite2[1]) || r1.include?(sprite2[1] + sprite2[0].width) ||
r2.include(sprite1[1]) || r2.include(sprite1[1] + sprite1[0].width)
assuming that the ranges are not endless.
There are a number of cases to consider, as pointed out by #muistooshort in a comment.
I assume that the elements of the ranges are integers, as in the example.
Code
The easiest way to determine if two ranges intersect is to negate an expression that determines if they don't intersect.
def overlap?(r1, r2)
return false if r1.size.zero? || r2.size.zero?
!(range_last(r1) < r2.begin || range_last(r2) < r1.begin)
end
def range_last(r)
return Float::INFINITY if r.end==Float::INFINITY || r.end.nil?
r.include?(r.end) ? r.end : r.end-1
end
Examples
overlap? 1..3, 2..5 #=> true
overlap? 1..3, 4..5 #=> false
overlap? 3..1, 0..5 #=> false
overlap? 1..4, 4..8 #=> true
overlap? 1...4, 4..8 #=> false
overlap? 4.., 1..4 #=> true
overlap? 4.., 1...4 #=> false
overlap? -Float::INFINITY..Float::INFINITY, 1..1 #=> true
overlap? -Float::INFINITY..7, 7..8 #=> true
overlap? -Float::INFINITY...7, 7..8 #=> false
overlap? -4..Float::INFINITY, -6..-4 #=> true
overlap? -4..Float::INFINITY, 6...-4 #=> false
Observe that the range 3..1 in the 3rd example is empty.
Note also:
range_last(1..) #=> Infinity
range_last(1..Float::INFINITY) #=> Infinity
range_last(1..3) #=> 3
range_last(1...3) #=> 2
If the two ranges, r1 and r2, are finite and of the two-dot variety one can replace range_last(r) with r.last.
If you do not have endless Ranges, maybe you could consider to use the Set intersection method Set#&:
require 'set'
def overlap?(a, b)
(a.to_set & b.to_set).any?
end
Aren't you guys making it too difficult ?
Maybe I miss something but the easy way to check for intersections is as follows.
#use the splat operator `*` to change our range to an array
r1 = *(1..5) # [1 , 2 , 3 , 4, 5]
r2 = *(4..8) # [4, 5, 6, 7, 8]
# and give the intersection with the intersection operator &
# if it is an empty array, they don't intersect
r1 & r2 # [4, 5]
# or
(r1 & r2).any? # true
Note: the order of the arrays doesn't matter for the result.
I have this code to return true if num is a power of 2.
def is_power_of_two?(num)
result = num.inject(0) {|n1, n2| n2 ** n1}
if result == num
true
else
false
end
end
p is_power_of_two?(16)
I keep getting an error though. How could I fix and simplify this code?
Clearly, n is a non-negative integer.
Code
def po2?(n)
n.to_s(2).count('1') == 1
end
Examples
po2? 0 #=> false
po2? 1 #=> true
po2? 32 #=> true
po2? 33 #=> false
Explanation
Fixnum#to_s provides the string representation of an integer (the receiver) for a given base. The method's argument, which defaults to 10, is the base. For example:
16.to_s #=> "16"
16.to_s(8) #=> "20"
16.to_s(16) #=> "10"
15.to_s(16) #=> "f"
It's base 2 we're interested in. For powers of 2:
1.to_s(2) #=> "1"
2.to_s(2) #=> "10"
4.to_s(2) #=> "100"
8.to_s(2) #=> "1000"
16.to_s(2) #=> "10000"
For a few natural numbers that are are not powers of 2:
3.to_s(2) #=> "11"
5.to_s(2) #=> "101"
11.to_s(2) #=> "1011"
We therefore wish to match binary strings that contain one 1.
Another Way
R = /
\A # match beginning of string ("anchor")
10* # match 1 followed by zero or more zeroes
\z # match end of string ("anchor")
/x # free-spacing regex definition mode
def po2?(n)
(n.to_s(2) =~ R) ? true : false
end
po2?(4) #=> true
po2?(5) #=> false
And one for the road
This uses Fixnum#bit_length and Fixnum#[]:
def po2?(n)
m = n.bit_length-1
n[m] == 1 and m.times.all? { |i| n[i].zero? }
end
po2? 0 #=> false
po2? 1 #=> true
po2? 32 #=> true
po2? 33 #=> false
Try:
def is_power_of_two?(num)
num != 0 && (num & (num - 1)) == 0
end
It is well explained here (for C#, but #GregHewgill's explanation applies here as well)
I would do something like this, using Ruby's Math module.
def power_of_two?(n)
Math.log2(n) % 1 == 0
end
Or, if you wanted to be really cool:
def power_of_two?(n)
(Math.log2(n) % 1).zero?
end
Some IRB output:
2.1.0 :004 > power_of_two?(2)
=> true
2.1.0 :005 > power_of_two?(32768)
=> true
2.1.0 :006 > power_of_two?(65536)
=> true
This method assumes that the input is a positive integer.
Source
Another way to solve this is to go the other way around than most of the answers here - we can use the number 1 to start and find out if the number is the power of two. Like this:
def power_of_two?(num)
product = 1
while product < num
product *= 2
end
product == num
end
We start with 1. Then we multiply the 1 by 2, and keep multiplying by 2 until the product is larger than num (product < num). Once we hit that condition, we stop, exit the loop, and check if it's equal to num (product == num). If it is, the num is the power of 2.
As was pointed out in the comments above, you were getting errors because you're trying to use the inject method on a non-iterable (an int). Here's a solution using the suggested log2
def is_power_of_two?(num)
result = Math.log2(num)
result == Integer(result)
end
Note: will fail with very big numbers close to binaries (like 2 ^ 64 - 1). A foolproof version (but slower) would be:
def is_power_of_two?(num)
while (num % 2 == 0 and num != 0)
num /= 2
end
num == 1
end
Please comment any improvements that any of you may find.
Here is another solution that uses recursion:
def power_of_2?(number)
return true if number == 1
return false if number == 0 || number % 2 != 0
power_of_2?(number / 2)
end
In my opinion, the easiest -- but maybe a little long -- way of doing what you need to do is just writing this recursive method like so:
def power_of_two?(number)
continue = true
if number == 1
return true
end
if number % 2 != 0
return false
else
while continue == true do
if number.to_f / 2.0 == 2.0
continue = false
return true
else
if number % 2 != 0
continue = false
return false
else
number /= 2
continue = true
end
end
end
end
end
One is a power of two (2^0), so it first checks if the number given is 1. If not, it checks if it is odd, because 1 is the only odd number that is a power of two.
If it is odd it returns false and moves on to the else statement. It will check if the number divided by 2 is two, because then it would obviously be a power of 2. It does this as a float, because 5/2 in Ruby would return 2.
If that is false, it then again checks if the number is odd -- unnecessary on the first round, necessary after that. If the number is not odd, it will divide the number by two and then do the loop another time.
This will continue until the program resolves itself by getting 2 or any odd number, and returns true or false, respectively.
I ran into this one in a bootcamp application prep. I'm not a math person and don't understand a few of these methods, so I wanted to submit a common sense approach for people like me. this requires little knowledge of math, except to know a number to the second power will be the result of some number multiplied by itself.
def is_power_of_two?(num)
num.times {|n| return true if (n+1) * (n+1) == num}
false
end
this method counts up to the num variable starting at 1 and returns true if (any of those numbers in the sequence multiplied by itself) is equal to num & if num is not 0 (more on that below).
example:
num = 9
1 * 1 == 9 #=> false
2 * 2 == 9 #=> false
3 * 3 == 9 #=> true
true is returned and method is finished running.
the #times method requires an integer > 0, so this edge case is "handled" by virtue of the fact that #times does nothing with "0" as the variable and returns false when outside of the #times iteration.
def power_of_two?(num)
num.to_s(2).scan(/1/).length == 1
end
I am trying to implement this function that checks if two strings are permutations of each other. The code itself is straightforward.
def permutation(a, b)
if a.length != b.length
return False
end
a = a.chars.sort.join
b = b.chars.sort.join
return a == b
end
a = "abcedff"
b = "acbedf"
puts (permutation(a, b).to_s)
However, when I try to run this file on the terminal, I keep getting an error that says
permutation.rb:3:in permutation': uninitialized constant False (NameError)
from permutation.rb:13:in'
I don't understand the reason for this.
Ruby is not Python. You want true and false, not True and False.
Don't over-complicate it. All you need to do is compare two character arrays. For example:
def permutation a, b
a.chars.sort == b.chars.sort
end
Given your corpus as posted, this yields:
a = "abcedff"
b = "acbedf"
permutation a, b
#=> false
permutation a, a
#=> true
The question is Create a function that takes three numbers as input and returns true or false depending on whether those three numbers can form a triangle. Three numbers can form a triangle if the sum of any two sides is greater than the third side.
my answer is:
def is_triangle(a,b,c)
if a+b > c
return true
elsif a+c>b
return true
elsif b+c>a
return true
else
return false
end
end
the thing is: my supposed false return keeps returning true. please help!
This logic should work for finding your triangle
def is_triangle?(a,b,c)
sorted = [a,b,c].sort
greatest_side = sorted.pop
greatest_side < sorted.sum
end
Yet another approach:
def is_triangle?(a,b,c)
[a,b,c].max < [a,b,c].sum/2.0
end
Or for Ruby outside of Rails:
def is_triangle?(a,b,c)
[a,b,c].max < [a,b,c].inject(:+)/2.0
end
Your problem is that unless all 3 numbers are 0 one of your ifs will always be true. What you want is something more like
def is_triangle(a,b,c)
a + b > c && a + c > b && b + c > a
end
is_triangle(3,6,8) #=> true
is_triangle(3,6,80) #=> false
Nothing you pass into this is going to return false. Your method is wrong.
You can tell if three sides make a triangle by finding the longest side and then adding the remaining two sides. If they are greater than the longest side, then the sides can make a traingle.
I suggest if you are sure your logic is correct change the method to
def is_triangle?(a, b, c)
a+b > c or b+c > a or c+a > b
end
But according to me it is not so the method should be
def is_triangle?(a, b, c)
a+b>c and b+c>a and c+a>b
end
Some points to note about ruby conventions:
Method which returns boolean ends with '?'
A ruby method returns the last evaluated expression, so writing return is redundant here.
puts " enter a, b and c values"
a=gets.to_i
b=gets.to_i
c=gets.to_i
if a+b > c
print true
elsif a+c>b
print true
elsif b+c>a
print true
else
print false
end
you can also use this code too.. this is much easy
This is also a solution to this problem :
if 2*[a,b,c].max < a + b + c
true
else
false
end
There are a variety of different inequalities theoretically possible:
http://en.wikipedia.org/wiki/Triangle_inequality