Reputation: 11107
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.
Upvotes: 2
Views: 137
Reputation: 8042
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.
Upvotes: 0
Reputation: 110675
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
Upvotes: 0
Reputation: 3760
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.
Upvotes: 2
Reputation: 153
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
Upvotes: 3
Reputation: 38645
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
.
Upvotes: 4
Reputation: 3310
result = nil
index = numbers.index(10)
if index and index > 0
result = numbers[(index - 1)]
end
result
# => 6
Upvotes: 2