0

I have a simple array like this:

stops = ["red_stop", "green_stop", "blue_stop", "yellow_stop", "purple_stop"]

I want to determine the distance (number of stops) between two 'stops'. I understand:

stops.index("purple_stop")

will return 4, but I don't understand how to write a Ruby method that will take any two stops as arguments and calculate the number of stops between them (e.g. the "red_stop" is 3 stops to the "yellow_stop"

2
  • 3
    You mean how to calculate stops.index("purple_stop") - stops.index("red_stop") ? Commented Sep 25, 2014 at 11:07
  • Yes, a = stop.index("purple_stop) b = stop.index("red_stop") puts a - b (but I want to write that as a Ruby method Commented Sep 25, 2014 at 11:10

4 Answers 4

2

It would be simply:

class Array
  def dist(a,b)
    (index(b) - index(a)).abs
  end
end

stops = ["red_stop", "green_stop", "blue_stop", "yellow_stop", "purple_stop"]
stops.dist('red_stop', 'blue_stop') #=> 2

Note however it will not work if your array has duplicates.

Sign up to request clarification or add additional context in comments.

2 Comments

If anyone else is wondering what the difference is between Array#index and Array#find_index, I can't tell that there is one. They appear to be identical. ruby-doc.org/core-2.1.3/Array.html#method-i-index
@thohl they are: Array.instance_method(:index) == Array.instance_method(:find_index) #=> true
1

use this:

stops = ["red_stop", "green_stop", "blue_stop", "yellow_stop", "purple_stop"]

def getdiff(source, destination, stops)
 (stops.index(destination) - stops.index(source)).abs
end

getdiff("red_stop", "yellow_stop", stops)

2 Comments

Perhaps because before you edited your answer, your method took too few arguments.
that was a mistake while I typed, I hve edited it now. Hopefully he removes his downvote
0

If you need all combinations:

stops = ["red_stop", "green_stop", "blue_stop", "yellow_stop", "purple_stop"]

stops.map.with_index { |s,i| [s,i] }
         .combination(2).each_with_object({}) { |((s1,d1),(s2,d2)),h|
           h[[s2,s1]] = h[[s1,s2]] = (d1-d2).abs }
  #=> {["red_stop",    "green_stop"] =>1, ["green_stop",  "red_stop"]   =>1,
  #    ["red_stop",    "blue_stop"]  =>2, ["blue_stop",   "red_stop"]   =>2,
  #    ["red_stop",    "yellow_stop"]=>3, ["yellow_stop", "red_stop"]   =>3,
  #    ["red_stop",    "purple_stop"]=>4, ["purple_stop", "red_stop"]   =>4,
  #    ["green_stop",  "blue_stop"]  =>1, ["blue_stop",   "green_stop"] =>1, 
  #    ["green_stop",  "yellow_stop"]=>2, ["yellow_stop", "green_stop"] =>2,
  #    ["green_stop",  "purple_stop"]=>3, ["purple_stop", "green_stop"] =>3, 
  #    ["blue_stop",   "yellow_stop"]=>1, ["yellow_stop", "blue_stop"]  =>1,
  #    ["blue_stop",   "purple_stop"]=>2, ["purple_stop", "blue_stop"]  =>2,
  #    ["yellow_stop", "purple_stop"]=>1, ["purple_stop", "yellow_stop"]=>1}

The steps are as follows:

a = stops.map.with_index { |s,i| [s,i] }
  #=> [["red_stop",    0], ["green_stop",  1], ["blue_stop", 2],
  #    ["yellow_stop", 3], ["purple_stop", 4]]

b = a.combination(2)
  #=> #<Enumerator: [["red_stop", 0], ["green_stop", 1], ["blue_stop", 2],
  #                  ["yellow_stop", 3], ["purple_stop", 4]]:combination(2)>

We can convert the enumerator b to an array to see its elements:

b.to_a
  #=> [[["red_stop",    0], ["green_stop",  1]],
  #    [["red_stop",    0], ["blue_stop",   2]],
  #    [["red_stop",    0], ["yellow_stop", 3]],
  #    [["red_stop",    0], ["purple_stop", 4]],
  #    [["green_stop",  1], ["blue_stop",   2]],
  #    [["green_stop",  1], ["yellow_stop", 3]],
  #    [["green_stop",  1], ["purple_stop", 4]],
  #    [["blue_stop",   2], ["yellow_stop", 3]],
  #    [["blue_stop",   2], ["purple_stop", 4]],
  #    [["yellow_stop", 3], ["purple_stop", 4]]]

Create a hash with the distances for each pair (not the decomposition of the block variables):

b.each_with_object({}) { |((s1,d1),(s2,d2)),h|
  h[[s2,s1]] = h[[s1,s2]] = (d1-d2).abs }
  #=> {["red_stop", "green_stop"]=>1, ["green_stop", "red_stop"]=>1,
  #    ...
  #    ["yellow_stop", "purple_stop"]=>1, ["purple_stop", "yellow_stop"]=>1}

Comments

0

In your example of stops, if your entries are considered to be bidirectional then the assumption is stops.dist(a,b) == stops.dist(b,a). If so, then you're getting good answers already, such as @BroiSatse above.

If your stops array represents nodes or states that transition directionally (e.g. in a loop or digraph) then you may need to have a dist method to be respect that. In other words, your array is implied to wrap around.

class Array
  def dist(a,b)
    (index(b)-index(a)) % self.size
  end
end

stops = ["red_stop", "green_stop", "blue_stop", "yellow_stop", "purple_stop"]
stops.dist('red_stop', 'blue_stop')  #=> 2 (as expected)
stops.dist('blue_stop', 'red_stop')  #=> 3 (not 2, since it's directional)

An example of traffic light states (where transition is directional):

lights = ['green', 'yellow', 'red']
lights.dist('green', 'red')      #=> 2
lights.dist('red', 'green')      #=> 1 (green light follows a red light)

This depends on the domain you're trying to encode. If direction is unimportant, then please ignore this answer! ;-) Good luck.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.