2012-07-31 8 views
7

Ich versuche, ein Array von Ganzzahlen zu einem Hash zu gruppieren, basierend auf dem, wo die einzelnen Werte in einen Bereich fallen. Grundsätzlich möchte ich ein Array in ein Histogramm mit fester Breite konvertieren.Ruby Array to Histogram: Wie gruppieren Zahlen nach Bereich?

Beispiel:

values = [1,3,4,4,4,4,4,10,12,15,18] 
bin_width = 3 

ich die Array-Werte in einem Bereich basierenden historgram von wo sie fallen in eine 3-gliedrige breiten Schaufel wie so zu gruppieren müssen:

{'0..2'=>[1,3],'3..5'=>[4,4,4,4,4],'6..8'=>[],'9..11'=>[10].... 

Ist gibt es eine einfache Ein-Zeilen-Lösung (vielleicht so etwas wie values.group_by{|x| #range calc}), die hier funktionieren würde?

+1

Fragen: 1) Ich denke, 3 in 3..5 sein sollte. 2) Warum verwenden Sie Strings als Schlüssel anstelle von echten Bereichen ?, 3) Sie brauchen auch die leeren Bereiche? – tokland

+3

sollte es keine Obsession sein, One-Liner zu schreiben. Streben nach Lösungen, die nur Ausdrücke beinhalten (das ist es, mit einem funktionalen Ansatz) ja, aus Angst, Zuweisungen, um ein paar Zeilen zu speichern, nein. – tokland

Antwort

10
values = [1, 7, 2, 8, 2] 
values.group_by { |x| x/3 }.map { |k, vs| [(3*k..3*k+2), vs] }.to_h 
#=> {0..2=>[1, 2, 2], 6..8=>[7, 8]} 

Wenn Sie wirklich die leeren Bereiche benötigen, glaube ich nicht, dass ein sauberer Einzeiler möglich ist. Aber das tun soll:

grouped = values.group_by { |x| x/3 } 
min, max = grouped.keys.minmax 
(min..max).map { |n| [(3*n..3*n+2), grouped.fetch(n, [])] }.to_h 
#=> {0..2=>[1, 2, 2], 3..5=>[], 6..8=>[7, 8]} 
+0

Hah, ich wollte nur etwas vorschlagen, das keine Facetten benötigt, und dann hast du deinen Beitrag aktualisiert. –

+1

@Michael, yeah Entschuldigung, in der Tat war mein Facetten-Snippet völlig falsch, map_by ist hier nicht nützlich, wir müssen die Schlüssel verarbeiten, nicht die Werte. So würdest du es schreiben? – tokland

4

ich kam mit einer ziemlich ineffizient, sondern ganz klarer Lösung:

ranges = 0.step(values.max, bin_width).each_cons(2).map { |s, e| Range.new(s, e, true) } 
values.group_by { |v| ranges.find { |r| r.cover? v } } 
+0

Danke für die allgemeinere Version, die sowohl mit 3-Unit-Bins funktioniert und sich an beliebigere Fälle anpassen kann. Genau das, was ich brauchte. – slothbear