|
1 | 1 | class Array |
2 | 2 |
|
3 | | - # Convert an array of values (which must respond to #succ) to an |
4 | | - # array of ranges. |
| 3 | + # Produces an array of ranges from the values in the array. |
| 4 | + # Contiguous values and overlapping ranges are merged. |
5 | 5 | # |
6 | | - # [3,4,5,1,6,9,8].to_ranges => [1..1,3..6,8..9] |
| 6 | + # Examples |
7 | 7 | # |
8 | | - # CREDIT: Adapted and debugged by Ryan Duryea |
9 | | - # from https://dzone.com/articles/convert-ruby-array-ranges |
| 8 | + # [1,2,3,6,7,8].to_ranges #=> [1..3, 6..8] |
| 9 | + # |
| 10 | + # [3,4,5,1,6,9,8].to_ranges #=> [1..6, 8..9] |
| 11 | + # |
| 12 | + # [10..15, 16..20, 21, 22].to_ranges #=> [10..22] |
| 13 | + # |
| 14 | + # Assumes inclusive ranges (i.e. 1..4) and range.first <= range.last. |
| 15 | + # |
| 16 | + # Works with integers, dates and strings. However, all the objects |
| 17 | + # in the array must be of the same class. |
| 18 | + # |
| 19 | + # CREDIT: monocle, Ryan Duryea |
10 | 20 |
|
11 | 21 | def to_ranges |
12 | | - array = self.compact.uniq.sort |
13 | | - ranges = [] |
14 | | - if !array.empty? |
15 | | - # Initialize the left and right endpoints of the range |
16 | | - left, right = array.first, nil |
17 | | - array.each do |obj| |
18 | | - # If the right endpoint is set and obj is not equal to right's successor |
19 | | - # then we need to create a range. |
20 | | - if right && obj != right.succ |
21 | | - ranges << Range.new(left,right) |
22 | | - left = obj |
| 22 | + array = compact.uniq.sort_by { |e| Range === e ? e.first : e } |
| 23 | + array.inject([]) do |c, value| |
| 24 | + unless c.empty? |
| 25 | + last = c.last |
| 26 | + last_value = (Range === last ? last.last : last) |
| 27 | + current_value = (Range === value ? value.first : value) |
| 28 | + if (last_value.succ <=> current_value) == -1 |
| 29 | + c << value |
| 30 | + else |
| 31 | + first = (Range === last ? last.first : last) |
| 32 | + second = [Range === last ? last.last : last, Range === value ? value.last : value].max |
| 33 | + c[-1] = [first..second] |
| 34 | + c.flatten! |
23 | 35 | end |
24 | | - right = obj |
| 36 | + else |
| 37 | + c << value |
25 | 38 | end |
26 | | - ranges << Range.new(left,right) |
27 | 39 | end |
28 | | - ranges |
29 | 40 | end |
| 41 | + |
| 42 | + alias arrange to_ranges |
| 43 | + alias rangify to_ranges |
| 44 | + |
30 | 45 | end |
0 commit comments