Array of indexes to array of ranges

前端 未结 7 943
执念已碎
执念已碎 2020-12-09 12:46

Ranges in ruby are pretty cool. I end up with arrays such as this:

geneRanges = [(234..25), (500..510), (1640..1653)]

And subsequently ha

相关标签:
7条回答
  • 2020-12-09 12:46

    (New and improved. Stays fresh in your refrigerator for up to two weeks!):

    a = [1, 2, 3, 10, 11, 20, 20, 4]
    
    ranges = a.sort.uniq.inject([]) do |spans, n|
      if spans.empty? || spans.last.last != n - 1
        spans + [n..n]
      else
        spans[0..-2] + [spans.last.first..n]
      end
    end
    
    p ranges    # [1..4, 10..11, 20..20]
    
    0 讨论(0)
  • 2020-12-09 12:49

    This is a straight crib of Wayne Conrads algorithm with a small tweak to make it work for other kinds of ranges, e.g. alphabetic

    def array_to_ranges(a)
    ranges = a.sort.uniq.inject([]) do |spans, n|
      if spans.empty? || spans.last.last.succ != n
        spans + [n..n]
      else
        spans[0..-2] + [spans.last.first..n]
      end
    end
    ranges
    end
    
    [
      [1..3, 10..11, 20..20, 4..4],
      [ "a".."c", "f".."h", "x".."z"],
      ["aa".."af"]
    ].each do |arange|
      p arange
      p array = arange.collect {|range| range.to_a}.flatten
      p array_to_ranges(array)
    end
    

    And the results of executing this are

    [1..3, 10..11, 20..20, 4..4]
    [1, 2, 3, 10, 11, 20, 4]
    [1..4, 10..11, 20..20]
    ["a".."c", "f".."h", "x".."z"]
    ["a", "b", "c", "f", "g", "h", "x", "y", "z"]
    ["a".."c", "f".."h", "x".."z"]
    ["aa".."af"]
    ["aa", "ab", "ac", "ad", "ae", "af"]
    ["aa".."af"]
    
    
    0 讨论(0)
  • 2020-12-09 12:57

    I've never seen anything in the Ruby language that does that, but here is some code that might help you do it yourself:

    http://snippets.dzone.com/posts/show/4677

    0 讨论(0)
  • 2020-12-09 13:02

    Here's an answer (adapted from this code) that is more than twice as fast as the other code posted here. Further, only this answer and the one by @Steve handle arrays of non-integers.

    class Array
      def to_ranges
        return [] if empty?
        [].tap do |ranges|
          init,last = first
          each do |o|
            if last && o != last.succ
              ranges << (init..last)
              init = o
            end
            last = o
          end
          ranges << (init..last)
        end
      end
    end
    

    Here are the benchmark results:

                     user     system      total        real
    steve        1.107000   0.000000   1.107000 (  1.106221)
    wayne        1.092000   0.000000   1.092000 (  1.099220)
    user229426   0.531000   0.000000   0.531000 (  0.523104)
    mladen1      0.780000   0.000000   0.780000 (  0.774154)
    mladen2      0.780000   0.000000   0.780000 (  0.792159)
    phrogz       0.218000   0.000000   0.218000 (  0.220044)
    

    All benchmarked code was adapted to remove sort.uniq for a fair comparison.

    0 讨论(0)
  • 2020-12-09 13:04

    Functional, not-very-readable solution:

    (a[0,1]+a.each_cons(2).reject{|i,j| j-i==1}.flatten+a[-1,1]).
      each_slice(2).map{|i,j| i..j}
    

    And a nice one:

    class Array
      # splits array to sub-arrays wherever two adjacent elements satisfy a condition
      def split_by
        each_cons(2).inject([[first]]){|a, (i, j)|
          a.push([]) if yield(i, j)
          a.last.push j
          a
        }
      end
    
      # uses split_by to split array to subarrays with consecutive elements, then convert to range
      def to_range
        split_by{|i,j| j-i!=1}.map{|a| a.first..a.last}
      end
    end
    
    [505, 506, 507, 600, 1647, 1648, 1649, 1650, 1651, 1654].split_by{|i,j| j-i!=1}
    #=> [[505, 506, 507], [600], [1647, 1648, 1649, 1650, 1651], [1654]]
    [505, 506, 507, 600, 1647, 1648, 1649, 1650, 1651, 1654].to_range
    #=> [505..507, 600..600, 1647..1651, 1654..1654]
    
    0 讨论(0)
  • 2020-12-09 13:09
    ar=[505, 506, 507, 600, 1647, 1648, 1649, 1650, 1651, 1654]
    def to_range(a)
      s=a[0]
      a.each_cons(2) do |a|
        if a[1]-a[0]!=1
            p s .. a[0]
            s=a[1]
        end
      end
      left=a.index(s)
      p a[left..-1][0]..a[left..-1][-1]
    end
    to_range(ar)
    
    0 讨论(0)
提交回复
热议问题