views:

35

answers:

4

In Ruby, how can I take an array of tokens representing either integers or ranges and parse them into an array of integers that include each integer and each element in each range?

Example: Given input [ "5", "7-10", "24", "29-31"]

I'd like to produce output [ 5, 7, 8, 9, 10, 24, 29, 30, 31 ]

Thanks.

+1  A: 

Something like the following should work. Just pass your input into the method and get an array of integers out. I've kept it intentionally verbose so you can see the logic.

Edit: I've added comments to the code.

def generate_output(input)
    output = []
    input.each do |element|
        if element.include?("-")
            # If the number is a range, split it
            split = element.split("-")
            # Take our split and turn it into a Ruby Range object, then an array
            output << (split[0].to_i..split[1].to_i).to_a
        else
            # If it's not a range, just add it to our output array
            output << element.to_i
        end
    end
    # Since our ranges will add arrays within the output array, calling flatten
    # on it will make it one large array with all the values in it.
    return output.flatten
end

Running this code on your example input produces your example output, so I believe it's spot on.

Mike Trpcic
@Mike, I was literally writing an identical answer! +1
Jacob Relkin
y'all are awesome! thanks very much!
jsinnott
+1  A: 

Well, this might need a bit of work, actually. I'll take a crack at it now:

def parse_argv_list(list)
   number_list = []
   list.each do |item|
      if item.include?('-')
         bounds = item.split('-')
         number_list.push((bounds[0].to_i..bounds[1].to_i).to_a)
      else
         number_list.push(item.to_i)
      end
   end
   number_list.flatten
end
Jacob Relkin
Terrific-- thanks for taking the time to answer.
jsinnott
+2  A: 
[ "5", "7-10", "24", "29-31"].map{|x| x.split("-").map{|val| val.to_i}}.map{ |y| Range.new(y.first, y.last).to_a}.flatten
Tim
Concise. Elegant. Fantastic. Thanks :)
jsinnott
A: 
>> [ "5", "7-10", "24", "29-31"].map{|x|x.gsub!(/-/,"..");x[".."]?(eval x).to_a : x.to_i}.flatten
=> [5, 7, 8, 9, 10, 24, 29, 30, 31]
ghostdog74
Nice! But the eval makes me nervous. I'm using this to parse ARGV so I think a user could inject malicious code pretty easily.
jsinnott
Nah, it should be safe to use if you sanitize ARGV, (eg, checking all of your ARGV for digits) before doing the actual flattening. Its not what you use, its how you use it that matters.
ghostdog74