Reputation: 43153
Let's say I have an array of random numbers in no particular order. Let's say these are the ID #'s for people who ran in a marathon, and they're added to the array in the order they finish, something like:
race1 = [8, 102, 67, 58, 91, 16, 27]
race2 = [51, 31, 7, 15, 99, 58, 22]
This is a simplified and somewhat contrived example, but I think it conveys the basic idea.
Now a couple questions:
First, how could I get the IDs that are before and after a particular entry? Let's say I am looking at runner 58 and I want to know who finished before and after him.
race1, runner58: previous finisher=67, next finisher=91
race2, runner58: previous finisher=99, next finisher=22
Second, if I'm looking at the person who finished first or last, how can I have the "next" or "previous" loop back around the array?
race1, runner8: previous finisher=27, next finisher=102
race2, runner22: previous finisher=58, next finisher=51
Lastly, I'd like to show what position each runner finished in. Given just the array and a value in it, how can I find out what 'ordinal' position it is in the array? Ie:
race1: runner8=1st, runner102=2nd, runner67=3rd ... runner27=last
Thanks very much!
Upvotes: 5
Views: 12892
Reputation: 2890
First & Second:
race1.rotate(race1.index(58) + 1).first # next
race1.rotate(race1.index(58) - 1).first # previous
Credit: saw this on this post: http://jamonholmgren.com/rubymotion-react-pattern/
Upvotes: 0
Reputation: 156662
For items #1 and #2 I would create a method which returns the previous and next elements by id, wrapping automatically for the first and last, e.g.:
class Array
def prev_next(id)
idx = self.index(id)
raise Error.new("no racer with id #{id}") unless idx
[self[idx-1], self[(idx+1)%self.size]]
end
end
race1.prev_next(58) # => [67, 91]
race1.prev_next(8) # => [27, 102]
Note that the only possible negative index, -1, actually rolls to the end thanks to Ruby's array.slice and by using the modulus of the array size we can adjust for wrapping over the end. For the third item, ordinalization can be done like so:
class Integer
def ordinalize
s = self.to_s
case s
when /1[123]$/ then s + 'th'
when /1$/ then s + 'st'
when /2$/ then s + 'nd'
when /3$/ then s + 'rd'
else s + 'th'
end
end
end
race1.each_with_index {|x,i| puts "runner#{x}=#{(i+1).ordinalize}"}
# runner8=1st
# runner102=2nd
# runner67=3rd
# ...
Upvotes: 5
Reputation: 31468
First & Second:
index = race1.find_index(58)
if !index.nil?
puts "#{race1[index-1], #{race1[index+1] || race1[0]}"
end
Lastly:
gem install linguistics
then
require 'linguistics'
Linguistics::use( :en )
race1.each_with_index {|runner, i| puts "runner#{runner}=#{(i+1).en.ordinal}"}
Upvotes: 7
Reputation: 778
The find_index
method will probably your best bet here.
runner_index = race.find_index finisher_number #find the index of the racer
previous_runner = race[runner_index - 1] #a negative index will wrap around
next_runner = race[runner_index + 1] || race[0] #use a null guard to wrap to the front
Upvotes: 2
Reputation: 13
Hello I don't know exactly what you want to do but if you are using ruby 1.9 you should look at the rotate method and if you are using ruby 1.8.7, you should try to build something around the index method like
race1[race1.index(58) +1] will give 98 (the next one)
race1[race1.index(58) -1] will give 67 (the previous one)
You need to tweak a little bit to simulate the rotate (by testing the value of the returned indice compared to the size of the array or to 0)
Upvotes: 0