2

I'm trying to parse a string of numbers and ranges joined by ",", and convert it to a numerical array. I have this as input: "1,3,6-8,5", and would like to have an array like this: [1,3,5,6,7,8].

I can only do it without the range, like this:

"12,2,6".split(",").map { |s| s.to_i }.sort #=> [2, 6, 12]

With a range, I cannot do it:

a = "12,3-5,2,6"
b = a.gsub(/-/, "..")               #=> "12,3..5,2,6"
c = b.split(",")                    #=> ["12", "3..5", "2", "6"]
d = c.sort_by(&:to_i)               #=> ["2", "3..5", "6", "12"]
e = d.split(",").map { |s| s.to_i } #>> Error

How can I do this?

I was also thinking to use the splat operator in map, but splat doesn't accept strings like [*(3..5)].

4 Answers 4

4
"12,3-5,2,6".
  gsub(/(\d+)-(\d+)/) { ($1..$2).to_a.join(',') }.
  split(',').
  map(&:to_i)
#⇒ [12, 3, 4, 5, 2, 6]
Sign up to request clarification or add additional context in comments.

2 Comments

For the benefit of the wider community it is usual to add some explanation to code only answers, otherwise they end up in the LQP queue for review like this one did....
Definitely the most easy answer to wrap my head around. No maps, and no if/then loops. Accepted.
3
"1,3,6-8,5".split(',').map do |str|
  if matched = str.match(/(\d+)\-(\d+)/)
    (matched[1].to_i..matched[2].to_i).to_a
  else
    str.to_i
  end
end.flatten

or

"1,3,6-8,5".split(',').each_with_object([]) do |str, output|
  if matched = str.match(/(\d+)\-(\d+)/)
    output.concat (matched[1].to_i..matched[2].to_i).to_a
  else
    output << str.to_i
  end
end

or strict

RANGE_PATTERN = /\A(\d+)\-(\d+)\z/
INT_PATTERN   = /\A\d+\z/

"1,3,6-8,5".split(',').each_with_object([]) do |str, output|
  if matched = str.match(RANGE_PATTERN)
    output.concat (matched[1].to_i..matched[2].to_i).to_a

  elsif str.match(INT_PATTERN)
    output << str.to_i

  else
    raise 'Wrong format given'
  end
end

Comments

1
"1,3,6-8,5".split(',').flat_map do |s|
  if s.include?('-')
    f,l = s.split('-').map(&:to_i)
    (f..l).to_a
  else
    s.to_i
  end
end.sort
  #=> [1, 3, 5, 6, 7, 8]

Comments

1
"1,3,6-8,5"
.scan(/(\d+)\-(\d+)|(\d+)/)
.flat_map{|low, high, num| num&.to_i || (low.to_i..high.to_i).to_a}
#=> [1, 3, 6, 7, 8, 5]

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.