2010-09-16 4 views
10

Диапазоны в рубине довольно прохладные. я в конечном итоге с массивами, например, как это:Массив индексов к массиву диапазонов

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

И затем должны удалить биты из них. Для этого 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] 

Им манипулируют, поэтому некоторые номера исключаются, а другие могут быть добавлены. Я могу в итоге:

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

Спасибо!

+1

более решения по: http://stackoverflow.com/questions/12360108 – tokland

+1

'(234..25)' является недопустимый диапазон. '(234..25) .to_a => []'. –

ответ

14

(Новый и улучшенный Туры свежа в вашем холодильнике до двух недель!):

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] 
+1

+1 Хорошее решение, однако значение внутри else должно быть 'spans [0 ..- 2] + [spans.last.first ..n] '(Отредактировано для исправления) –

+0

@Steve Weet, Спасибо. Хороший улов. –

+0

Отлично - спасибо! Проверьте также обобщение Стива Вэя и ответ Младена Яблановича дальше! –

1

Я никогда не видел ничего на языке Ruby, который делает это, но вот код, который может помочь вам сделать это самостоятельно:.

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

1
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) 
8

Функциональный, не-очень-читаемый решение:

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

Это довольно элегантно :) –

+0

SpaceBeforeModifierKeyword от RuboCop обнаружил, что перед 'if' не было пробела до' if' в 'a.push ([]), если yield (i, j)'. Я не знал, что это действительно Рубин. O_o –

+0

Rubocop также предлагает использовать 'each_with_object', а не' inject'. –

5

Это прямая хлев алгоритма 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"] 

+1

Это очень круто. Представьте, что вы пытаетесь сделать это на языке, который не выполнял утиную печать. –

5

Вот ответ (адаптировано из 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 для справедливого сравнения.

0
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! 
+0

~~ Вы не можете цеплять 'sort!' !!!! ~~ Извините, что на самом деле 'sort!' Похоже, что по какой-то причине безопасно цепочки. Большинство других методов мутации, заканчивающихся на '!', Не являются (например, gsub !, uniq !, и т. Д.). Поэтому я просто сжимаю, когда вижу, что кто-либо использует возвращаемое значение любого метода, заканчивающегося на '!' –

+0

@JackCasey, конечно, вы можете цепь любого из перечисленных методов (sort !, gsub! uniq!). Но в некоторых случаях это может быть бессмысленным – fl00r

+0

Конечно, я имел в виду, что это ловушка иногда :) –

Смежные вопросы