SwiftMango
SwiftMango

Reputation: 15284

Ruby: how to find the next match in an array

I have to search an item in an array and return the value of the next item. Example:

a = ['abc.df','-f','test.h']
i = a.find_index{|x| x=~/-f/}
puts a[i+1]

Is there any better way other than working with index?

Upvotes: 0

Views: 229

Answers (4)

Darshan Rivka Whittle
Darshan Rivka Whittle

Reputation: 34031

The reason something like array.find{something}.next doesn't exist is that it's an array rather than a linked list. Each item is just it's own value; it doesn't have a concept of "the item after me".

@tokland gives a good solution by iterating over the array with each pair of consecutive items, so that when the first item matches, you have your second item handy. There are strong arguments to be made for the functional style, to be sure. Your version is shorter, though, and I'd argue that yours is also more quickly and easily understood at a glance.

If the issue is that you're using it a lot and want something cleaner and more to the point, then of course you could just add it as a singleton method to a:

def a.find_after(&test)
  self[find_index(&test).next]
end

Then

a.find_after{|x| x=~/-f/}

is a clear way to find the next item after the first match.

All of that said, I think @BenjaminCox makes the best point about what appears to be your actual goal. If you're parsing command line options, there are libraries that do that well.

Upvotes: 3

dbenhur
dbenhur

Reputation: 20408

Your solution working with indexes is fine, as others have commented. You could use Enumerable#drop_while to get an array from your match on and take the second element of that:

a = ['abc.df','-f','test.h']
f_arg = a.drop_while { |e| e !~ /-f/ }[1]

Upvotes: 0

tokland
tokland

Reputation: 67860

A classical functional approach uses no indexes (xs.each_cons(2) -> pairwise combinations of xs):

xs = ['abc.df', '-f', 'test.h']
(xs.each_cons(2).detect { |x, y| x =~ /-f/ } || []).last
#=> "test.h"

Using Enumerable#map_detect simplifies it a litte bit more:

xs.each_cons(2).map_detect { |x, y| y if x =~ /-f/ }
#=> "test.h"

Upvotes: 3

Benjamin Cox
Benjamin Cox

Reputation: 6120

I don't know of a cleaner way to do that specific operation. However, it sure looks like you're trying to parse command-line arguments. If so, I'd recommend using the built-in OptionParser module - it'll save a ton of time and hair-pulling trying to parse them yourself.

This article explains how it works.

Upvotes: 1

Related Questions