Andrew
Andrew

Reputation: 43153

Ruby: next / previous values in array, loop array, array position

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

Answers (5)

jjnevis
jjnevis

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

maerics
maerics

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

Jonas Elfström
Jonas Elfström

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

Andy
Andy

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

tranquiliste
tranquiliste

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

Related Questions