2011-12-24 11 views
7

mi piacerebbe prendere l'input come ad esempio:Come si riassume la matrice di numeri interi come una matrice di intervalli?

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

e trasformarlo in qualcosa di simile al seguente:

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

Ogni sotto-array rappresenta una gamma di numeri interi.

+2

stai chiedendo se c'è il codice per fare questo già? Stai chiedendo perché stai provando a lanciare da solo e avere problemi nell'implementarlo? – bobbymcr

+0

Sto arrotolando il mio. Sembra che ci siano sempre modi interessanti per implementare questo tipo di cose in Ruby. – Larsenal

+0

Con quali condizioni si suppone di costruire gli intervalli? – cvshepherd

risposta

19

approccio funzionale utilizzando 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]] 

Come funziona: una volta indicizzato, elementi consecutivi dell'array hanno la stessa x - idx, in modo da utilizzare tale valore al pezzo (raggruppamento di elementi consecutivi) dell'array di input. Finalmente abbiamo solo bisogno di prendere il primo e l'ultimo elemento di ogni gruppo per costruire le coppie.

+0

Questo sembra davvero bello. Completamente dimenticato del nuovo metodo pezzo. – cvshepherd

+2

E per fare un altro passo, '.map {| min, max | min == max? min: min .. max} 'risulterà in:' [1..2, 4..7, 9, 13] '. –

+0

Oppure, cambia '[pairs.first [0], pairs.last [0]]' a 'pairs.first [0] .. pairs.last [0]' per ottenere intervalli in tutte le posizioni: '[1. .2, 4..7, 9..9, 13..13] '. –

0

Un altro approccio

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 

Simile al metodo di Larsenal ma usando iniettare per gestire la roba noiosa.

3

Hmm, beh, non è tokland's capolavoro, ma penso che potrebbe essere una buona soluzione semplice ...

[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

Questo è quasi direttamente dalla documentazione enumerable#slice_before metodo:

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] 

Questo dovrebbe funzionare con caratteri, date, qualsiasi cosa con un metodo .succ.

2

Una soluzione ancora più semplice che @tokland's very nice one sta usando chunk_while:

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

Nota: chunk_while è stato introdotto in Ruby 2.3

Problemi correlati