Get element in array just below matching value - arrays

I have a simple ruby array and I want to select the element in the array just below the matching value.
numbers = [1,2,3,4,5,6,10]
The matching value I have is 10 but I want to be able to instead get the value before 10 which is 6.
numbers.some_magical_ruby_method {|n| n == 10} # I hope to return 6 since it's the element before 10
My question is what Ruby method exists for me to select the value before the matching value.

You could use Array#take with Array#index:
> numbers.take(numbers.index(10).to_i).last
=> 6
If a value is not found then the returned value is nil.

You can extend Array class with this method
class Array
def previous_element el
each_cons(2) {|prev, curr| return prev if curr == el }
end
end

numbers[numbers.index(10) - 1]
=> 6

result = nil
index = numbers.index(10)
if index and index > 0
result = numbers[(index - 1)]
end
result
# => 6

def number_before numbers, num
idx = numbers.index(num)
return numbers[idx - 1] unless (idx.nil? || idx == 0)
return nil
end
> numbers = [1,2,3,4,5,6,10]
> number_before numbers, 10 #=> 6
> numbers = [1,2,3]
> number_before numbers, 10 #=> nil
> numbers = [1,10,6]
> number_before numbers, 10 #=> 1
> numbers = [10,6,1]
> number_before numbers, 10 #=> nil
Find the index of 10, and returning the previous element, or nil if the number is not found or previous element is not found. The idx == 0 case is important, because the array index -1 will wrap around to the front in ruby.

def prev_nbr(numbers, nbr)
n = nil
enum = numbers.to_enum
loop do
return n if enum.peek == nbr
n = enum.next
end
nil
end
previous_nbr(numbers, 4) #=> 3
previous_nbr(numbers, 8) #=> nil
previous_nbr(numbers, 1) #=> nil

This answer gives the result as the value of the element at the index before the requested value:
numbers[((arr_ind=numbers.index(10)).to_i > 0) ? arr_ind-1 : numbers.length]
This could also be written as this (using nil instead of the numbers[numbers.length] result):
((arr_ind=numbers.index(10)).to_i > 0) ? numbers[arr_ind-1] : nil
Neither of these solutions suffer the problem of "wrapping" found when used with the simple nil.to_i solutions or when the requested value is at the beginning of the array, such as when the value to search is 0 or not found in the array. This solution avoids artificial looping and excess memory usage.
The only side effect is that the arr_ind variable is either created or overwritten if it already exists.
This short test demonstrates the results of searching for each of the numbers in the range (-1..13):
numbers = [1,2,3,4,5,6,10]
def answer(arr,element)
arr[((arr_ind=arr.index(element)).to_i > 0) ? arr_ind-1 : arr.length]
end
answers = [nil, nil, nil, 1, 2, 3, 4, 5, 6, nil, nil, nil]
(-1..13).each_with_index do |number, i|
puts "#{answer(numbers,number) == answers[i] ? 'Pass' : 'Fail'}: #{number}"
end
The output from this test shows:
Pass: -1
Pass: 0
Pass: 1
Pass: 2
Pass: 3
Pass: 4
Pass: 5
Pass: 6
Fail: 7
Pass: 8
Pass: 9
Fail: 10
Pass: 11
Pass: 12
Pass: 13
"Pass" means that the test result meets expectations and passes; "Fail" means that it did not meet expectations. Expected values are in the answers array, corresponding one-to-one with the values in the test range.

Related

How do I find the unique number in an array and return only that number in ruby?

There is an array with some numbers. All numbers are equal except for one. I'm trying to get this type of thing:
find_uniq([ 1, 1, 1, 2, 1, 1 ]) == 2
find_uniq([ 0, 0, 0.55, 0, 0 ]) == 0.55
I tried this:
def find_uniq(arr)
arr.uniq.each{|e| arr.count(e)}
end
It gives me the two different values in the array, but I'm not sure how to pick the one that's unique. Can I use some sort of count or not? Thanks!
This worked:
def find_uniq(arr)
return nil if arr.size < 3
if arr[0] != arr[1]
return arr[1] == arr[2] ? arr[0] : arr[1]
end
arr.each_cons(2) { |x, y| return y if y != x }
end
Thanks pjs and Cary Swoveland.
I would do this:
[ 1, 1, 1, 2, 1, 1 ]
.tally # { 1=>5, 2=>1 }
.find { |_, v| v == 1 } # [2, 1]
.first # 2
Or as 3limin4t0r suggested:
[ 1, 1, 1, 2, 1, 1 ]
.tally # { 1=>5, 2=>1 }
.invert[1] # { 5=>1, 1=>2 } => 2
The following doesn't use tallies and will short circuit the search when a unique item is found. First, it returns nil if the array has fewer than 3 elements, since there's no way to answer the question in that case. If that check is passed, it works by comparing adjacent values. It performs an up-front check that the first two elements are equal—if not, it checks against the third element to see which one is different. Otherwise, it iterates through the array and returns the first value it finds which is unequal. It returns nil if there is not a distinguished element in the array.
def find_uniq(arr)
return nil if arr.size < 3
if arr[0] == arr[1]
arr.each.with_index do |x, i|
i += 1
return arr[i] if arr[i] != x
end
elsif arr[1] == arr[2]
arr[0]
else
arr[1]
end
end
This also works with non-numeric arrays such as find_uniq(%w(c c c d c c c c)).
Thanks to Cary Swoveland for reminding me about each_cons. That can tighten up the solution considerably:
def find_uniq(arr)
return nil if arr.size < 3
if arr[0] != arr[1]
return arr[1] == arr[2] ? arr[0] : arr[1]
end
arr.each_cons(2) { |x, y| return y if y != x }
end
For all but tiny arrays this method effectively has the speed of Enumerable#find.
def find_uniq(arr)
multi = arr[0,3].partition { |e| e == arr.first }
.sort_by { |e| -e.size }.first.first
arr.find { |e| e != multi }
end
find_uniq [1, 1, 1, 2, 1, 1] #=> 2
find_uniq [0, 0, 0.55, 0, 0] #=> 0.55
find_uniq [:pig, :pig, :cow, :pig] #=> :cow
The wording of the question implies the array contains at least three elements. It certainly cannot be empty or have two elements. (If it could contain one element add the guard clause return arr.first if arr.size == 1.)
I examine the first three elements to determine the object that has duplicates, which I assign to the variable multi. I then am able to use find. find is quite fast, in part because it short-circuits (stops enumerating the array when it achieves a match).
If
arr = [1, 1, 1, 2, 1, 1]
then
a = arr[0,3].partition { |e| e == arr.first }.sort_by { |e| -e.size }
#=> [[1, 1, 1], []]
multi = a.first.first
#=> 1
If any of these:
arr = [2, 1, 1, 1, 1, 1]
arr = [1, 2, 1, 1, 1, 1]
arr = [1, 1, 2, 1, 1, 1]
apply then
a = arr[0,3].partition { |e| e == arr.first }.sort_by { |e| -e.size }
#=> [[1, 1], [2]]
multi = a.first.first
#=> 1
Let's compare the computational performace of the solutions that have been offered.
def spickermann1(arr)
arr.tally.find { |_, v| v == 1 }.first
end
def spickermann2(arr)
arr.tally.invert[1]
end
def spickermann3(arr)
arr.tally.min_by(&:last).first
end
def pjs(arr)
if arr[0] == arr[1]
arr.each.with_index do |x, i|
i += 1
return arr[i] if arr[i] != x
end
elsif arr[1] == arr[2]
arr[0]
else
arr[1]
end
end
I did not include #3limin4t0r's solution because of the author's admission that it is relatively inefficient. I did include, however, include two variants of #spikermann's answer, one ("spickermann2") having been proposed by #3limin4t0r in a comment.
require 'benchmark'
def test(n)
puts "\nArray size = #{n}"
arr = Array.new(n-1,0) << 1
Benchmark.bm do |x|
x.report("Cary") { find_uniq(arr) }
x.report("spickermann1") { spickermann1(arr) }
x.report("spickermann2") { spickermann2(arr) }
x.report("spickermann3") { spickermann3(arr) }
x.report("PJS") { pjs(arr) }
end
end
test 100
Array size = 100
user system total real
Cary 0.000032 0.000009 0.000041 ( 0.000029)
spickermann1 0.000022 0.000015 0.000037 ( 0.000019)
spickermann2 0.000017 0.000002 0.000019 ( 0.000016)
spickermann3 0.000019 0.000002 0.000021 ( 0.000018)
PJS 0.000042 0.000025 0.000067 ( 0.000034)
test 10_000
Array size = 10_000
user system total real
Cary 0.001101 0.000091 0.001192 ( 0.001119)
spickermann1 0.000699 0.000096 0.000795 ( 0.000716)
spickermann2 0.000794 0.000071 0.000865 ( 0.000896)
spickermann3 0.000776 0.000081 0.000857 ( 0.000781)
PJS 0.001140 0.000113 0.001253 ( 0.001300)
test 1_000_000
Array size = 1_000_000
user system total real
Cary 0.061148 0.000787 0.061935 ( 0.063022)
spickermann1 0.043598 0.000474 0.044072 ( 0.044590)
spickermann2 0.044909 0.000663 0.045572 ( 0.046371)
spickermann3 0.042907 0.000210 0.043117 ( 0.043162)
PJS 0.072766 0.000226 0.072992 ( 0.073168)
I attribute the apparent superiority of #spickermann's answer to the fact that Enumerable#tally has no block to evaluate (unlike, for example, Enumerable#find in my answer).
Your code can be fixed by using find instead of each:
def find_uniq(arr)
arr.uniq.find { |e| arr.count(e) == 1 }
end
However this is quite inefficient since uniq needs to iterate the full collection. After finding the unique values the arr collection is iterated 1 or 2 more times by count (assuming there are only two unique values), depending on the position of the values in the uniq result.
For simple solution I suggest looking at the answer of spickermann which only iterates the full collection once.
For your specific scenario you could technically increase performance by short-circuiting the tally. This is done by manually tallying and breaking the loop if the tally contains 2 distinct values and at least 3 items are tallied.
def find_uniq(arr)
tally = Hash.new(0)
arr.each_with_index do |item, index|
break if tally.size == 2 && index >= 3
tally[item] += 1
end
tally.invert[1]
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

Return a Boolean if an element occurs three times in a row in an array

I am trying to write a method that takes an array and returns trueif there is an element that occurs three times in a row or false if it doesn't. I can't think of the syntax. Would you use count? See the example below.
def got_three?(array)
end
got_three?([1,2,2,3,4,4,4,5,6]) would return true as 4 shows up three times in a row.
Toying with the new Ruby 2.3.0 method chunk_while:
def got_three?(array)
array.chunk_while(&:==).any?{|g| g.size >= 3}
end
With Enumerable#chunk:
def got_three?(xs)
xs.chunk(&:itself).any? { |y, ys| ys.size >= 3 }
end
Not so smart but a naive one (using a instead of array since it is long):
a.each_index.any?{|i| a[i] == a[i + 1] and a[i + 1] == a[i + 2]}
I assume you don't have any nil in the array.
An alternative which may be more performant (as per #sawa's comments)...
def got_three?(array)
(0..(array.count-2)).any?{|i|array[i] == array[1+1] && array[i] == array[i+2]}
end
Look, ma, no indices!
def ducks_in_a_row?(arr, n)
cnt = 0
last = arr.first
arr.each do |d|
if d==last
cnt += 1
return true if cnt==n
else
last = d
cnt = 1
end
end
false
end
ducks_in_a_row?([1,2,3,4,5,6,6,7,7,7], 3)
#=> true
def got_three?(array)
array.each_cons(3).map{|g|g.uniq.length == 1}.any?
end
or as #wandmaker suggests...
def got_three?(array)
array.each_cons(3).any?{|g|g.uniq.length == 1}
end
Here is my take on this problem - I tried to make it generic, hopefully efficient so that the loop terminates as soon as n-consecutive elements are found.
def got_consecutive?(array, n = 3)
case array.size
when 0...n
return false
when n
return array.uniq.size == n
else
array[n..-1].each_with_object(array[0...n]) do |i, t|
(t.uniq.size == 1 ? (break t) : (t << i).shift)
end.uniq.size == 1
end
end
p got_consecutive?([])
#=> false
p got_consecutive?([1,2])
#=> false
p got_consecutive?([1,2,2,3,2,3,3,3], 3)
#=> true
p got_consecutive?([1,2,2,3,2,3,3,3], 4)
#=> false
p got_consecutive?([1,2,2,3,2,3,3,3,3,3,4,4,4,4], 5)
#=> true
The code takes care of border cases first such as when array did not have n elements in which case answer is obviously false, and another one being when the array had only n elements - in which case just a uniqueness check would suffice.
For cases where array size is greater than n, the code uses Enumerable#each_with_object with the initial object being an array of n elements from the array - this array is used also as temporary work area to track n consecutive elements and perform a check whether all those elements are same or not.

Efficient way to select items < value from sorted array

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.

Check for consecutive numbers

I have an array m of integers. I'm looking for a method to check if the elements of m are consecutive. Is there a way to test for consecutive numbers?
I came up with this code intended to work when the array length is four:
m.count == 4 && (m.max-m.min) == 3
which incorrectly returns true for [1,1,1,4] or [0,0,0,3].
Enumerable has a really handy method called each_cons that works like this:
[1,2,3,4].each_cons(2).to_a # => [ [1, 2], [2, 3], [3, 4] ]
That is, it yields each consecutive set of n elements. In our case n is 2.
Of course, as the name implies, it returns an Enumerator, so we can chain it with other Enumerable methods like all?:
def four_consecutive?(arr)
return false unless arr.size == 4
arr.each_cons(2).all? {|a, b| b == a + 1 }
end
four_consecutive?([2,3,4,5]) # => true
four_consecutive?([2,2,2,5]) # => false
four_consecutive?([1,2,3,4,5]) # => false
This method has the advantage above others that, because all? short-circuits as soon as the block returns false, it will only test numbers until it finds a pair that don't meet the condition (b == a + 1). Of course, with only four elements this doesn't really make a difference—unless you're calling this method thousands of times in situation where performance matters.
You can try this:
a == (a.min..a.max).to_a && a.count == 4
This only works when the array is in increasing order. [3, 4, 5, 6] will pass but [4, 3, 5, 6] won't.
The Answer is based on mathematical problem for Sum of consecutive integers
Sum = n∗(n+1)/2
Code:
def check_sum_match?(arr)
m = arr.min - 1
n = arr.max
sum1 = arr.inject{|sum, x| sum = sum + x}
sum2 = (n*(n+1) - m*(m+1))/2
sum1 == sum2
end
arr = [5,6,7,8]
if arr.count == 4 && check_sum_match?(arr)
puts 'Yes condition matches'
else
puts 'Invalid Array'
end
# valid arrays are
# [4,6,5,7], [4,5,6,7], etc
Tentative Explanation:
If a is the array and n is the required size:
def size_and_consecutive?(a, n)
a == (a.first..(a.first+n-1)).to_a
end
size_and_consecutive? [3,4,5,6], 4
#=> true
size_and_consecutive? [4,3,5,6], 4
#=> false
size_and_consecutive? [3,4,5], 4
#=> false
A compact solution that I could come up with is as follows:
def consec(arr)
is_of_proper_length = (arr.size == 4)
if(is_of_proper_length)
are_consec = true
arr.each_cons(2) {|x,y| are_consec = false unless ((y - x) == 1)}
end
is_of_proper_length && are_consec
end
Output:
consec([1,2,3,4])
=> true
2.2.0 :051 > consec([0,0,0,0])
=> false
2.2.0 :052 > consec([4,6,5,7])
=> true
2.2.0 :053 > consec([4,5,6,7])
=> true
2.2.0 :054 > consec([5,6,7,8])
=> true
2.2.0 :055 > consec([2,2,2,5])
=> false
2.2.0 :056 > consec([2,3,4,5])
=> true
2.2.0 :057 > consec([1,2,3,4,5])
=> false

Resources