索引数组到范围数组

发布于 2024-09-24 05:04:26 字数 684 浏览 2 评论 0 原文

ruby 中的范围非常酷。 我最终得到这样的数组:

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)]

谢谢!

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

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

And subsequently have to remove bits of them. For that I:

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]

They get manipulated, so some numbers get excluded, and others may be added. I may end up with this:

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

How can I convert this back into a compact array of ranges? It seems that the inverse function should exist? I would expect it to return something like this:

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

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

烟花肆意 2024-10-01 05:04:26

(新的和改进的。在冰箱中可保持新鲜长达两周!):

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]

(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]
狼性发作 2024-10-01 05:04:26

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

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

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]
明媚殇 2024-10-01 05:04:26

这是 Wayne Conrads 算法的直接婴儿床,经过一些小调整,使其适用于其他类型的范围,例如字母顺序

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"]

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"]

苹果你个爱泡泡 2024-10-01 05:04:26

这是一个答案(改编自此代码),其速度是其他代码的两倍多发布在这里。此外,只有这个答案和@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 以进行公平比较。

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.

§普罗旺斯的薰衣草 2024-10-01 05:04:26

我从来没有在 Ruby 语言中见过任何可以做到这一点的代码,但这里有一些代码可以帮助您自己完成此操作:

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

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

煮酒 2024-10-01 05:04:26
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)
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)
—━☆沉默づ 2024-10-01 05:04:26
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!
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]

and for not sorted arrays you can presort it:

array.sort!.uniq!
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文