2011-12-24 19 views
7

Chciałbym wziąć wejściowe takie jak:Jak podsumować tablicę liczb całkowitych jako tablicę zakresów?

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

i przekształcić go w coś jak poniżej:

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

Każdy sub-array reprezentuje zakres liczb całkowitych.

+2

Pytasz czy istnieje kod to zrobić już? Pytasz, ponieważ próbujesz wywrócić własną i masz problemy z jej implementacją? – bobbymcr

+0

Toczę własne. Wydaje się, że zawsze istnieją interesujące sposoby implementacji tego rodzaju rzeczy w Ruby. – Larsenal

+0

W jakich warunkach należy budować zakresy? – cvshepherd

Odpowiedz

19

podejście funkcjonalną 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]] 

Jak to działa: raz indeksowane, kolejne elementy tablicy mają taką samą x - idx, więc możemy użyć tej wartości do fragmentu (grupowanie kolejnych pozycji) tablicy wejście. Na koniec musimy po prostu wziąć pierwsze i ostatnie elementy z każdej grupy, aby zbudować pary.

+0

To wygląda naprawdę ładnie. Zupełnie zapomniałem o nowej metodzie porcji. – cvshepherd

+2

I jeszcze jeden krok, '.map {| min, max | min == maks.? min: min .. max} 'spowoduje:' [1..2, 4..7, 9, 13] '. –

+0

Lub zmień '[pary.pierwsze [0], pary.last [0]]' na 'pairs.first [0] .. pairs.last [0]', aby uzyskać zakresy we wszystkich pozycjach: '[1. .2, 4..7, 9..9, 13..13] ". –

0

Innym podejściem

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 

podobne metody Larsenal, ale za pomocą wstrzyknięcia zarządzać nudne rzeczy.

3

Hmm, dobrze, że nie jest tokland's arcydzieło, ale myślę, że może to być dobre rozwiązanie proste ...

[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

To niemal prosto z dokumentacją enumerable#slice_before metoda:

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] 

Powinno to działać ze znakami, datami, dowolnymi metodami .succ.

2

Jeszcze prostsze rozwiązanie niż @tokland's very nice one korzysta chunk_while:

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

Uwaga: chunk_while został wprowadzony w Ruby 2.3

Powiązane problemy