Efficient way to select items < value from sorted array - arrays

I have an array with sorted numbers, eg.
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
Is there a way to select items < 5, without traversing the whole array?
Eg. the following will traverse the whole array and check each item. But, for a sorted array, it could break as soon as it hits an item >= 5.
arr.select { |p| p < 5 }
I've tried variations with break, next and return without success.
arr.select { |p| p < 5; break if p >= 5 } # = nil
arr.select { |p| p < 5; next if p >= 5 } # = []
arr.select { |p| p < 5; return if p >= 5 } # LocalJumpError
What's a good way to achieve this?

I think you can use the take_while method for that.

Here is another way of doing it:
> arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
> index = arr.index(5)
#=> 4
> arr[0..index]
#=> [1, 2, 3, 4, 5]

First, why did your solutions not work? select accepts a block, and it throws away all elements for which the block returns a false-ish value (nil or false) and keeps all the ones for which the block returns a true-ish value.
arr.select { |p| p < 5; break if p >= 5 } # = nil
Here, you call p < 5 but then you just ignore its return value. In Ruby, the return value of a block is the value of the last expression evaluated inside the block. The last expression evaluated here is break if p >= 5. Since there is no else case, it will return nil (which is false, i.e. means "do not keep this element") for all elements less than 5, and then it will break (which means "abort and return nil").
IOW: you are telling select to throw away all elements less than 5 and then to abort.
arr.select { |p| p < 5; next if p >= 5 } # = []
In this case, again, you are returning nil for all elements less than 5, and for all elements greater than or equal to 5, you are returning the argument to next … but there is no argument, ergo, you are also returning nil for all elements greater than 5.
IOW: you are telling select to throw away all elements less than 5 and then you tell it to throw away all elements greater than or equal to 5, or, put another way, you tell it to throw away all elements … and you are still iterating the entire array.
This doesn't make much sense anyway. next is implied in a block, just like return is implied in a method. (In fact, next is for blocks what return is for methods), so all you have done is add a superfluous keyword.
arr.select { |p| p < 5; return if p >= 5 } # LocalJumpError
Again, the same as before … you are telling select to throw away all elements less than 5, and then you are returning from the enclosing method. But there is no enclosing method, so Ruby raises a LocalJumpError.
You could "fix" your second solution this way:
arr.select { |p| if p < 5 then true else next end }
I.e. you need to actually return true (or something true-ish) for elements less than 5. But, like I said, next is implied anyway, so you can just leave it out, which means this is equivalent to
arr.select { |p| if p < 5 then true else nil end }
Which, since select treats false and nil the same is equivalent to
arr.select { |p| if p < 5 then true else false end }
Which is of course just the same as
arr.select { |p| p < 5 }
Which, because of the symmetry of < and > is just the same as
arr.select(&5.method(:>))
Anyway, the correct solution for searching in a sorted array would be to use a binary search, which will not need to iterate the array at all, and will only need O(log(n)) comparisons:
arr.bsearch_index(&5.method(:<=))
This will search for the "index of the element at the next biggest index right of the last number that is less than 5". Then you can use Array#[] to extract the slice of elements you want
arr[0...arr.bsearch_index(&5.method(:<=))]
Note that Array#bsearch_index is new in 2.3.0, I believe, until then, you can use Range#bsearch instead:
arr[0...(0...arr.size).bsearch {|i| arr[i] >= 5 }]

You can also do that using Enumerable#lazy with Enumerable#slice_while:
arr.lazy.slice_before { |i| i >= 5 }.first
#=> [1, 2, 3, 4]
By making slice_before lazy, first terminates the slicing after the first slice is obtained.

Related

do I make this method work. The goal is to create a new set or array in which each element in the original set is multiplied by 2

def new_array(nums)
i = 0
double = []
while i <= nums.length
new_array = double[i] * 2
double << new_array
i += 1
end
return double
end
puts new_array[2, 4, 6, 8]
You can use map to create a new array with each value multiplied by 2.
def array_times_two(original_array)
original_array.map { |n| n * 2 }
end
array_times_two([2, 4, 6, 8])
=> [4, 8, 12, 16]
new_array = double[i] * 2
You are mixing up your variables. new_array is not an array, it is the name of your method. double is an empty array, so double[i] does not exist. Also, it loops once too many (the loop starts to count at zero). Try this:
while i < nums.length # not <=
double << nums[i] * 2
i += 1
end
Futhermore, call your method like this: puts new_array([2, 4, 6, 8]) , so with brackets (or put a space between the method and the argument), otherwise ruby itself will be mixed up.
Doubling the elements of an array is better done using map or map! (depending on what exactly you want to achieve), but I guess you try to implement the algorithm in this hard way (without using the Ruby core library) as a programming exercise, so I focus on your question why your approach does not work:
In your code, when you execute
new_array = double[i] * 2
double[i] is always nil, because you initialize double to the empty array and update it later, after the multiplication. You obviously can't multiply nil and will get an error message from this line.

How to square an array of numbers in ruby with while without `each` `map` or `collect` methods?

I'm new to coding in RUBY. I'm trying to write a method that squares each element in an array of numbers and returns a new array of these numbers squared. Trying to use while loop and NOT use each, collect, or map. Having trouble understanding how to index/loop each individual element of array and square is (**).
This is what makes sense to me but I know its wrong.
def square_array(numbers)
count = 0
while count < numbers.length do
numbers.index ** 2
end
square_array(numbers)
end
Will anyone please help me? Thanks!
The easy way to do it is map, of course:
def square_array(numbers)
numbers.map { |e| e ** 2 }
end
But here's what you have to do to do the same with a while loop (which is good practice).
Create an array to contain the transformed data.
Create a counter (you've done that).
Set up your while loop (as you have it, except you don't need the do at the end).
Write a statement that squares the array element whose index is the same as your counter, and pushes that result into the array you created in step 1.
Increment your counter by 1 (you forgot to do that, so you'll be getting an endless loop since count will always equal zero).
Return the array you created in step 1.
That will do it for you! See if you can put that together, rather than me just giving you the code.
def square_array(numbers)
# Allocate an array with the same size as `numbers`
# so that the runtime does not have to resize it from time to time
result = Array.new(numbers.size)
# The index
i = 0
while i < numbers.size
# Fill the result array
result[i] = numbers[i] ** 2
# and don't forget to increase the index,
# otherwise the loop will run forever.
i += 1
end
# Return the result array
result
end
The more functional approach would be to use recursion.
fun =
->(acc = [], arr, map, fun) {
arr.empty? ? acc : fun.(acc << map.(arr.shift), arr, map, fun)
}
#⇒ #<Proc:0x000055ab64333fa0#(pry):12 (lambda)>
And for any mapper (e. g. square root,) use it like:
fun.([1,2,3,4,5], ->(e) { e ** 2 }, fun)
#⇒ [1, 4, 9, 16, 25]
Please note! This approach mutates the initial array, so it should be explicitly array.dup’ed before passing to the function. To eliminate the necessity to pass the function itself through and leave the initial array intact, we would need a wrapper.
fun =
->(acc = [], arr, map, fun) {
arr.empty? ? acc : fun.(acc << map.(arr.shift), arr, map, fun)
}
#⇒ #<Proc:0x000055ab64333fa0#(pry):12 (lambda)>
mapper = ->(arr, map) { fun.([], arr.dup, map, fun) }
And use it like:
arr = [1,2,3,4,5]
mapper.(arr, ->(e) { e ** 2 })
#⇒ [1, 4, 9, 16, 25]
arr
#⇒ [1, 2, 3, 4, 5]
def sq(arr)
enum = arr.each
a = []
loop do
n = enum.next
a << n*n
end
a
end
sq [1, 2, 3, 4]
#=> [1, 4, 9, 16]
See Array#each, Kernel#loop and Enumerator#next. One could use Kernel#to_enum (documented in Object) in place of Array#each.
Using a for loop?
ary = [1,2,3]
res = []
for n in ary do
res << n ** 2
end
res
#=> [1, 4, 9]
But better you stick with map.
Here is my solution:
def square_array(numbers)
new_array = []
counter = 0
while counter < numbers.length()
new_array.push(numbers[counter] * numbers[counter])
counter += 1
end
return new_array
end
Without using each, map, or collect.
def square_array(array)
new_array = []
array.length.times do |index|
new_array.push(array[index] ** 2)
end
new_array
end

What is the smallest jump I can make to move through a range, when all jumps are the same length and some positions cannot be landed on?

Working through CodeFights. This is problem 4 on level 5 https://codefights.com/arcade/intro/level-5/XC9Q2DhRRKQrfLhb5 :
You are given an array of integers representing coordinates of
obstacles situated on a straight line.
Assume that you are jumping from the point with coordinate 0 to the
right. You are allowed only to make jumps of the same length
represented by some integer.
Find the minimal length of the jump enough to avoid all the obstacles.
Example
For inputArray = [5, 3, 6, 7, 9], the output should be
avoidObstacles(inputArray) = 4.
Check out the image below for better understanding:
Input/Output
[time limit] 4000ms (rb) [input] array.integer inputArray
Non-empty array of positive integers.
Constraints: 2 ≤ inputArray.length ≤ 10, 1 ≤ inputArray[i] ≤ 40.
[output] integer
The desired length.
The natural method for making this happen seems like it would be step. Here is my code:
def avoidObstacles(arr)
jumps = (arr.min..arr.max+1).to_a - arr
full_map = (0..arr.max+1)
jumps.each do |j|
return j if (full_map.step(j).to_a & arr).empty?
end
end
A clearer way to write this:
def avoidObstacles(arr)
jumps = (arr.min..arr.max+1).reject{|n| arr.include?(n)}
full_map = (0..arr.max+1)
jumps.each do |j|
return j if full_map.step(j).none?{|n|arr.include?(n)}
end
end
I pass all of the visible tests given on the website:
Input: inputArray: [5, 3, 6, 7, 9] Expected Output: 4
Input: inputArray: [2, 3] Expected Output: 4
Input: inputArray: [1, 4, 10, 6, 2] Expected Output: 7
But I get tripped up on one of the hidden tests. I borrowed a solution from another user, which works in all cases:
def avoidObstacles a
obs = a.each_with_object(Hash.new(false)){|v, h| h[v]=true}
m = a.max
(1..m+1).each do |j|
return j if (0...m/j+1).all?{ |i| obs[i*j] == false }
end
m
end
I'm not quite sure where this borrowed solution succeeds and mine fails.
Thanks ahead of time for the help. I'm still quite new to coding and appreciate your time.
avoidObstacles [3,5,7] #=> 4
but it should be 2. You want
def avoid_obstacles(arr)
return nil if arr.empty? or arr.max == 1
jumps = (2..arr.max+1).to_a - arr
full_map = (0..arr.max+1)
jumps.each do |j|
return j if (full_map.step(j).to_a & arr).empty?
end
end
avoid_obstacles [3,5,7]
#=> 2
I changed the name of the method because the Ruby convention is to use snake-case for the names of methods and variables.
The other person's solution works because it starts the search at j = 1. (It could have started at j=2 because j = 1 will always fail.
I tried this in Java using the input constraints given (so did not use input validation)
int avoidObstacles(int[] inputArray) {
Arrays.sort(inputArray);
//start from 2 because jump length 1 will always fail
int minJump=2;
while(validateJumpValue(inputArray, minJump)){
minJump++;
}
return minJump;
}
//check if all the successive jumps can avoid touching obstacles
boolean validateJumpValue(int[] arr, int jump){
int successiveJump=jump;
while(Arrays.binarySearch(arr, successiveJump)<0 && successiveJump <= arr[arr.length-1]) {
successiveJump+=jump;
}
return successiveJump<=arr[arr.length-1];
}
Try this:
def avoidObstacles(inputArray):
obs = sorted(inputArray)
jump_dist = 1
obstacle_hit = True
while(obstacle_hit):
obstacle_hit = False
jump_dist += 1
for i in range(0, len(obs)):
#### 3%4 and 5%4 and 6%4 and 7%4 and 9%4 =! 0
if obs[i] % jump_dist == 0:
obstacle_hit = True
break
return jump_dist

Using memoization for storing values in ruby array

For a short array the following function works well. It's supposed to return the first array pair that whe sum is equal to a given integer. However, if the array has a length upwards of 10 million elements, the request times out, because (I think) is storing thousands of values in the variable I create in the first line. I know I have to use memoization (||=) but have no idea how to use it.
array1 = [1,2,3,4,5,6,7]
number = 3
array2 = [1,2,3.....n] # millions of elements
combos = array1.combination(2).to_a
(combos.select { |x,y| x + y == number }).sort.first
I need to gather all possible pairs to sort them, I'm using select to go through the entire list and not stop at the first pair that returns true.
This is one of the possible solutions.
def sum_pairs(ints, s)
seen = {}
for i in ints do
return [s-i, i] if seen[s-i]
seen[i] = true
end
nil
end
def find_smallest(arr, nbr)
first, *rest = arr.sort
until rest.empty?
matching = rest.bsearch { |n| n == nbr - first }
return [first, matching] unless matching.nil?
first, *rest = rest
end
nil
end
arr = [12, 7, 4, 5, 14, 9]
find_smallest(arr, 19) #=> [5, 14]
find_smallest(arr, 20) #=> nil
I've used the method Array#bsearch (rather than Enumerable#find to speed up the search for an element equal to nbr - first (O(log rest.size) vs. O(rest.size)).

Ruby: Find index of next match in array, or find with offset

I want to find further matches after Array#find_index { |item| block } matches for the first time. How can I search for the index of the second match, third match, and so on?
In other words, I want the equivalent of the pos argument to Regexp#match(str, pos) for Array#find_index. Then I can maintain a current-position index to continue the search.
I cannot use Enumerable#find_all because I might modify the array between calls (in which case, I will also adjust my current-position index to reflect the modifications). I do not want to copy part of the array, as that would increase the computational complexity of my algorithm. I want to do this without copying the array:
new_pos = pos + array[pos..-1].find_index do |elem|
elem.matches_condition?
end
The following are different questions. They only ask the first match in the array, plus one:
https://stackoverflow.com/questions/11300886/ruby-how-to-find-the-next-match-in-an-array
https://stackoverflow.com/questions/4596517/ruby-find-next-in-array
The following question is closer, but still does not help me, because I need to process the first match before continuing to the next (and this way also conflicts with modification):
https://stackoverflow.com/questions/9925654/ruby-find-in-array-with-offset
A simpler way to do it is just:
new_pos = pos
while new_pos < array.size and not array[new_pos].matches_condition?
new_pos += 1
end
new_pos = nil if new_pos == array.size
In fact, I think this is probably better than my other answer, because it's harder to get wrong, and there's no chance of future shadowing problems being introduced from the surrounding code. However, it's still clumsy.
And if the condition is more complex, then you end up needing to do something like this:
new_pos = pos
# this check is only necessary if pos may be == array.size
if new_pos < array.size
prepare_for_condition
end
while new_pos < array.size and not array[new_pos].matches_condition?
new_pos += 1
if new_pos < array.size
prepare_for_condition
end
end
new_pos = nil if new_pos == array.size
Or, God forbid, a begin ... end while loop (although then you run into trouble with the initial value of new_pos):
new_pos = pos - 1
begin
new_pos += 1
if new_pos < array.size
prepare_for_condition
end
end while new_pos < array.size and not array[new_pos].matches_condition?
new_pos = nil if new_pos == array.size
This may seem horrible. However, supposing prepare_for_condition is something that keeps being tweaked in small ways. Those tweaks will eventually get refactored; however, by that time, the output of the refactored code will also end up getting tweaked in small ways that don't belong with the old refactored code, but do not yet seem to justify refactoring of their own - and so on. Occasionally, someone will forget to change both places. This may seem pathological; however, in programming, as we all know, the pathological case has a habit of occurring only too often.
Here is one way this can be done. We can define a new method in Array class that will allow us to find indexes that match a given condition. The condition can be specified as block that returns boolean.
The new method returns an Enumerator so that we get the benefit of many of the Enumerator methods such next, to_a, etc.
ary = [1,2,3,4,5,6]
class Array
def find_index_r(&block)
Enumerator.new do |yielder|
self.each_with_index{|i, j| yielder.yield j if block.call(i)}
end
end
end
e = ary.find_index_r { |r| r % 2 == 0 }
p e.to_a #=> [1, 3, 5]
p e.next
#=> 1
p e.next
#=> 3
ary[2]=10
p ary
#=> [1, 2, 10, 4, 5, 6]
p e.next
#=> 5
e.rewind
p e.next
#=> 1
p e.next
#=> 2
Note: I added a new method in Array class for demonstration purpose. Solution can be adapted easily to work without the monkey-patching
Of course, one way to do it would be:
new_pos = pos + (pos...array.size).find_index do |index|
elem = array[index]
elem.matches_condition?
end
However, this is clumsy and easy to get wrong. For example, you may forget to add pos. Also, you have to make sure elem isn't shadowing something. Both of these can lead to hard-to-trace bugs.
I find it hard to believe that an index argument to Array#find_index and Array#index still hasn't made it into the language. However, I notice Regexp#match(str,pos) wasn't there until version 1.9, which is equally surprising.
Suppose
arr = [9,1,4,1,9,36,25]
findees = [1,6,3,6,3,7]
proc = ->(n) { n**2 }
and for each element n in findees we want the index of the first unmatched element m of arr for which proc[n] == m. For example, if n=3, then proc[3] #==> 9, so the first matching index in arr would be 0. For the next n=3 in findees, the first unmatched match in arr is at index 4.
We can do this like so:
arr = [9,1,4,1,9,36,25]
findees = [1,6,3,6,3,7]
proc = ->(n) { n**2 }
h = arr.each_with_index.with_object(Hash.new { |h,k| h[k] = [] }) { |(n,i),h| h[n] << i }
#=> {9=>[0, 4], 1=>[1, 3], 4=>[2], 36=>[5], 25=>[6]}
findees.each_with_object([]) { |n,a| v=h[proc[n]]; a << v.shift if v }
#=> [1, 5, 0, nil, 4, nil]
We can generalize this into a handy Array method as follow:
class Array
def find_indices(*args)
h = each_with_index.with_object(Hash.new {|h,k| h[k] = []}) { |(n,i),h| h[n] << i }
args.each_with_object([]) { |n,a| v=h[yield n]; a << v.shift if v }
end
end
arr.find_indices(*findees) { |n| n**2 }
#=> [1, 5, 0, nil, 4, nil]
arr = [3,1,2,1,3,6,5]
findees = [1,6,3,6,3,7]
arr.find_indices(*findees, &:itself)
#=> [1, 5, 0, nil, 4, nil]
My approach is not much different from the others but perhaps packaged cleaner to be syntactically similar to Array#find_index . Here's the compact form.
def find_next_index(a,prior=nil)
(((prior||-1)+1)...a.length).find{|i| yield a[i]}
end
Here's a simple test case.
test_arr = %w(aa ab ac ad)
puts find_next_index(test_arr){|v| v.include?('a')}
puts find_next_index(test_arr,1){|v| v.include?('a')}
puts find_next_index(test_arr,3){|v| v.include?('a')}
# evaluates to:
# 0
# 2
# nil
And of course, with a slight rewrite you could monkey-patch it into the Array class

Resources