2011-01-11 14 views
13

Dato un array ordinato di n numeri interi, come il seguente:calcolare le differenze tra gli elementi dell'array

ary = [3, 5, 6, 9, 14] 

ho bisogno di calcolare la differenza tra ogni elemento e l'altro elemento della matrice. Utilizzando l'esempio precedente, avrei finito con:

[2, 1, 3, 5] 

La matrice può avere inizio 0, 1 o più elementi in essa, ei numeri sarò trattamento sarà molto più grande (sarò utilizzando timestamp di epoca). Ho provato il seguente:

times = @messages.map{|m| m.created_at.to_i} 
left = times[1..times.length-1] 
right = times[0..times.length-2] 
differences = left.zip(right).map { |x| x[0]-x[1]} 

Ma la mia soluzione di cui sopra non è né ottimale, né ideale. Qualcuno può darmi una mano?

risposta

38
>> ary = [3, 5, 6, 9, 14] #=> [3, 5, 6, 9, 14] 
>> ary.each_cons(2).map { |a,b| b-a } #=> [2, 1, 3, 5] 

Edit: Sostituito inject con map.

+0

Questo è incredibilmente conciso. Grazie per la fantastica risposta. –

+4

Bello! Prima non conoscevo 'each_cons'. Questa è una versione un po 'più breve della stessa: 'ary.each_cons (2) .map {| a, b | b-a} '. – Heikki

+0

Come nota a margine: è importante dare un nome alle astrazioni: [(a, b), (b, c), (c, d), ...] sono chiamate "combinazioni a coppie". E infatti enumerable.each_cons (2) .map è il modo migliore per farlo in Ruby (grazie agli enumeratori tutti i metodi each_xyz possono ora essere usati "funzionalmente") – tokland

1

Un'alternativa:

a.map.with_index{ |v,i| (a[i+1] || 0) - v }[0..-2] 

non funziona in Ruby 1.8, dove map richiede un blocco invece di restituire un enumeratore.

7

Simile ma più conciso:

[3, 5, 6, 9, 14].each_cons(2).collect { |a,b| b-a } 
+2

Ancora più conciso se si sostituisce 'collect' con' map':) Si noti che 'each_cons' non è disponibile in 1.8.6, ma è disponibile in 1.8.7. – Phrogz

+0

Ovviamente hai ragione, 'map' è meglio di' inject' qui. –

Problemi correlati