[ACCEPTED]-What is the best way to convert a Ruby string range to a Range object-ruby

Accepted answer
Score: 39


Score: 14

But then just do

ends = '20080201..20080229'.split('..').map{|d| Integer(d)}

anyway I don't recommend 1 eval, for security reasons

Score: 7

Inject with no args works well for two element 1 arrays:

rng='20080201..20080229'.split('..').inject { |s,e| s.to_i..e.to_i }

Of course, this can be made generic

class Range
  def self.from_ary(a)
    a.inject{|s,e| s..e }

rng = Range.from_ary('20080201..20080229'.split('..').map{|s| s.to_i})
rng.class  # => Range
Score: 2

assuming you want the range to iterate properly 1 through months etc, try

require 'date'

ends = '20080201..20080229'.split('..').map{|d| Date.parse(d)}
(ends[0]..ends[1]).each do |d|
  p d.day
Score: 1

Ranger uses regex to validate strings with no 1 SQL injection fear, and then eval.

Score: 0

Combining @Purfideas answer with another 10 answer somewhere on StackOverflow, I solved 9 this by also surrounding the code with an 8 input check, so the only thing used is a 7 valid enumerable

if !value[/^[0-9]+\.\.[0-9]+$/].nil?
    ends = value.split('..').map{|d| Integer(d)}
    value = ends[0]..ends[1]

It essentially rewrites 6 your string value to a enumerable value. This 5 comes in handy if you add a enumerable field 4 in a yaml config file.

If you need it for 3 your application, you could extend the regex 2 with an optional third literal dot, that 1 could be optional.

Score: 0

If we do it like

v= "20140101..20150101"
raise "Error: invalid format: #{v}" if /\d{8}\.\.\d{8}/ !~ v
r= eval(v)

and the attacker has a way 7 of bypassing the raise check (simply by 6 means of manipulating the runtime to disable 5 exceptions) then we can get a dangerous 4 eval which will potentially destroy the 3 universe.

So for the sake of reducing attack 2 vectors, we check the format, and then do 1 the parsing manually, then check the results

v= "20140101..20150101"
raise "Error: invalid format: #{v}" if /\d{8}\.\.\d{8}/ !~ v
r= Range.new(*v.split(/\.\./).map(&:to_i))
raise "Error: invalid range: #{v}" if r.first> r.last
Score: 0

Here suppose you want to store the hash 7 as a system constant value and fetch it 6 in any model. The hash key will be a range 5 value.

hash_1 = {1..5 => 'a', 6..12 => 'b', 13..67 => 'c', 68..9999999 => 'd'}

Then create the system constant with 4 value as hash_1.to_json. .to_json will convert 3 your hash object to JSON object. Now inside 2 the code create a new hash hash_2,

JSON.parse(SystemConstant.get('Constant_name')).each{|key,val| temp_k=key.split('..').map{|d| Integer(d)}; hash_2[temp_k[0]..temp_k[1]] = val}

The new 1 hash_2 will be the required hash_1

Score: 0

I had a similar requirement although in 12 my case the strings were in two possible 11 formats, occasionally they were single number 10 strings such as "7", other times 9 they were ranges such as "10-14". Either 8 way I wanted to turn the string into an 7 Enumerable collection of numbers.

My approach 6 (inspired by the highest voted answer) was:

def numbers(from_string:)
  if from_string.include?('-')
    return Range.new(*from_string.split('-').map(&:to_i))
    return [from_string.to_i] # put number in an array so we can enumerate over it

It can also be done 5 as a (long) one-liner if you think that's 4 more readable:

from_string.include?('-') ? Range.new(*from_string.split('-').map(&:to_i)) : [from_string.to_i]

I was processing a long list 3 of known strings, not dealing with arbitrary 2 user input so this doesn't guard against 1 malicious input.

More Related questions