I have a sorted array of integers, and want all subsets of consecutive integers to be replaced with a range, so that:
[1,2,3,4,5,6,7,8,9,10,42]
is replaced with:
[1..10,42]
It's a list of rows from a Google spreadsheet that the user did not enter properly, and that could not be imported into another system's database. There are a lot of rows, and often dozens in sequence. It's much easier for a user to see that rows 3201-3379 are all bad, rather than having to read every single row number individually.
I'm not asking for "Array of integers into array of ranges".
I know how to write the code, and I'm happy to do so, but not if someone has already done it. My current code is not elegant and I'm not going to bother refactoring it if there's something well tested and optimized already out there.
I expect this question has been asked at SO, but the answer in not in my Rolodesk. It's easy enough to do, however.
arr = [1, 2, 3, 5, 6, 7, 8, 10, 12, 13, 14, 17, 18, 20]
arr.slice_when { |x,y| y > x+1 }.
map { |a| a.size == 1 ? a.first : a.first..a.last }
#=> [1..3, 5..8, 10, 12..14, 17..18, 20]
We first compute the enumerator
enum = arr.slice_when { |x,y| y > x+1 }
#=> #<Enumerator: #<Enumerator::Generator:0x...0000...fe0>:each>
By converting enum
to an array we can see the elements that will be generated by the enumerator and fed to map
:
enum.to_a
#=> [[1, 2, 3], [5, 6, 7, 8], [10], [12, 13, 14], [17, 18], [20]]