代码之家  ›  专栏  ›  技术社区  ›  Yannick Wurm

从索引数组到范围数组

  •  10
  • Yannick Wurm  · 技术社区  · 16 年前

    红宝石的靶场很酷。 我最终得到如下数组:

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

    genePositions = geneRanges.collect {|range| range.entries }.flatten
    => [500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 1640, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653]
    

    他们被操纵,所以一些数字被排除在外,而另一些则可能被加上。最后我可能会说:

    [505, 506, 507, 600, 601, 602, 603, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, 1654]
    

    [(505..507), (600..603), (1643..1654)]
    

    谢谢!

    7 回复  |  直到 14 年前
        1
  •  14
  •   Steve Weet    16 年前

    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]
    
        2
  •  8
  •   Andrew Grimm Alex Wayne    13 年前

    功能性、可读性不强的解决方案:

    (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}
    

    一个很好的例子:

    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]
    
        3
  •  5
  •   Steve Weet    16 年前

    这是一个直接的韦恩康拉德算法婴儿床与一个小的调整,使其适用于其他种类的范围,如字母

    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
    

    [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"]
    
    
        4
  •  5
  •   Community Mohan Dere    9 年前

    下面是一个答案(改编自 this code )这比这里发布的其他代码快一倍多。此外,只有这个答案和 the one by @Steve 处理非整数数组。

    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
    

    以下是基准测试结果:

                     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)
    

    所有基准代码都进行了调整,以删除 sort.uniq

        5
  •  1
  •   Wayne Conrad    16 年前

    在Ruby语言中,我从来没有见过这样做的东西,但这里有一些代码可以帮助您自己做到这一点:

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

        6
  •  1
  •   ghostdog74    16 年前
    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)
    
        7
  •  0
  •   fl00r    15 年前
    array = [505, 506, 507, 600, 601, 602, 603, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, 1654]
    array.inject([]){ |a, e| a[-1] && a[-1].last && a[-1].last == e-1 ? a[-1] = (a[-1].first..e) : a << (e..e); a }
    #=> [505..507, 600..603, 1643..1654]
    

    对于未排序的数组,可以对其进行预排序:

    array.sort!.uniq!
    
    推荐文章