Created
September 6, 2012 14:16
-
-
Save RoUS/3656728 to your computer and use it in GitHub Desktop.
[Ruby 1.8.7] Reduce consecutive elements in an array to ranges
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Array | |
# | |
# Return an array with all occurrences of 2 or more logically | |
# successive elements replaced with ranges, and disjoint elements | |
# as-is. | |
# | |
# @param [Boolean] sorted | |
# If this parameter can be evaluated as a Boolean 'true' value, the | |
# current array will be copied and the copy sorted before | |
# processing. If {#enrange} is passed a block, it will be passed | |
# along to the {Array#sort} method. | |
# @raise [ArgumentError] | |
# if the <tt>sorted</tt> parameter is true and the array cannot be | |
# sorted. | |
# @yieldparam [Object] a | |
# Element for comparison by block passed to {Array#sort}. | |
# @yieldparam [Object] b | |
# Element for comparison by block passed to {Array#sort}. | |
# @example | |
# ary = [1, 2, 3, 4, 5] | |
# ary.enrange => [1..5] | |
# @example | |
# ary = [5, 4, 3, 2, 1] | |
# ary.enrange => [5, 4, 3, 2, 1] | |
# @example | |
# ary = [5, 4, 3, 2, 1] | |
# ary.enrange(true) => [1..5] | |
# @example | |
# ary = [1, 2, 3, 4, 5, "a", "b", "c", "d", "e"] | |
# ary.enrange => ArgumentError raised | |
# @example | |
# ary = [1, 2, 3, 4, 5, "a", "b", "c", "d", "e"] | |
# ary.enrange { |a,b| a.to_s <=> b.to_s } => [1..5, "a".."e"] | |
# @example | |
# ary = [1, "a", 2, "b", 3, "c", 4, "d", 5, "e"] | |
# ary.enrange { |a,b| a.to_s <=> b.to_s } => [1, "a", 2, "b", 3, "c", 4, "d", 5, "e"] | |
# @example | |
# ary = [1, "a", 2, "b", 3, "c", 4, "d", 5, "e"] | |
# ary.enrange(true) { |a,b| a.to_s <=> b.to_s } => [1..5, "a".."e"] | |
# @note | |
# The example block may produce results other than expected if | |
# integer values have differing numbers of digits -- because "1" | |
# will be compared with "10" rather than 1 with 10. | |
# | |
def enrange(sorted=false, &block) | |
ary = sorted ? self.dup.sort(&block) : self | |
results = ary.inject([]) do |memo,elt| | |
unless (memo.empty? || (elt != [ *memo[-1] ].last.succ)) | |
# | |
# If there's already something in the results array, check the | |
# last element to see if this value can logically be appended | |
# to it (_i.e._, is a successor to the current endpoint | |
# value). | |
# | |
memo[-1] = [ *memo.last ][0] .. elt | |
else | |
# | |
# Not a successor, or maybe the results array is empty -- just | |
# append this value as-is. It may or may not form the seed of | |
# a new range. | |
# | |
memo << elt | |
end | |
memo | |
end | |
return results | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Pass 2. More direct, though perhaps slightly less clear -- and no internal comments. :-/