thank_you
thank_you

Reputation: 11107

Get element in array just below matching value

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

Answers (7)

Michael Gaskill
Michael Gaskill

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

Cary Swoveland
Cary Swoveland

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

Maxxx
Maxxx

Reputation: 1179

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

Upvotes: 3

Anand
Anand

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

Vadim Sergeevich
Vadim Sergeevich

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

vee
vee

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

guitarman
guitarman

Reputation: 3310

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

Upvotes: 2

Related Questions