2011-12-24 3 views
7

Я хотел бы взять вход, такие как:Как суммировать массив целых чисел как массив диапазонов?

[1,2,4,5,6,7,9,13] 

и превратить его в нечто вроде следующего:

[[1,2],[4,7],[9,9],[13,13]] 

Каждый суб-массив представляет собой набор целых чисел.

+2

Вы спрашиваете, если есть код, чтобы сделать это уже? Вы спрашиваете, потому что вы пытаетесь катиться самостоятельно и не можете его реализовать? – bobbymcr

+0

Я катаюсь самостоятельно. Кажется, всегда есть интересные способы реализовать подобные вещи в Ruby. – Larsenal

+0

По каким условиям должны быть построены диапазоны? – cvshepherd

ответ

19

Функциональный подход с использованием Enumerable#chunk:

xs.enum_for(:chunk).with_index { |x, idx| x - idx }.map do |diff, group| 
    [group.first, group.last] 
end 
# => [[1, 2], [4, 7], [9, 9], [13, 13]] 

Как это работает: когда индексируются, последовательные элементы массива имеют одинаковый x - idx, поэтому мы используем это значение для фрагмента (группировку последовательных элементов) входной массив. Наконец, нам просто нужно взять первый и последний элементы каждой группы для построения пар.

+0

Это выглядит очень хорошо. Полностью забыл о новом методе куска. – cvshepherd

+2

И еще один шаг: '.map {| min, max | min == max? min: min .. max} 'приведет к:' [1..2, 4..7, 9, 13] '. –

+0

Или измените '[pairs.first [0], pairs.last [0]]' на 'pairs.first [0] .. pairs.last [0]', чтобы получить диапазоны во всех позициях: '[1. .2, 4..7, 9..9, 13..13] '. –

0

Другой подход

def summarize(x) 
    x.inject([]) do |acc, value| 
    if acc.last && acc.last[1] + 1 == value 
     acc.last[1] = value 
     acc 
    else 
     acc << [value,value] 
    end 
    end 
end 

Подобно методу Larsenal, но с помощью инъекции, чтобы управлять скучные вещи.

3

Хм, ну, это не tokland's шедевр, но я думаю, что это может быть хорошим простое решение ...

[1,2,4,5,6,7,9,13].inject([]) do |m, v| 
    if m.last.to_a.last == v.pred 
    m[-1][-1] = v 
    else 
    m << [v, v] 
    end 
    m 
end 
4

Это почти прямо из документации enumerable#slice_before метода:

ar = [1,2,4,5,6,7,9,13] 
prev = ar[0] 
ar.slice_before{|e|prev,prev2 = e,prev; prev2.succ != e}.map{|a|a.first..a.last} 
#=> [1..2, 4..7, 9..9, 13..13] 

Это должно работать с символами, датами, с помощью метода .succ.

2

Еще проще, чем решение @tokland's very nice one использует chunk_while:

xs.chunk_while { |a, b| a + 1 == b }.map do |seq| 
    [seq.first, seq.last] 
end 

Примечание: chunk_while был введен в Рубине 2.3

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