rubyvis 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +0 -0
- data/History.txt +13 -1
- data/Manifest.txt +19 -0
- data/examples/antibiotics/antibiotics.rb +96 -0
- data/examples/antibiotics/antibiotics_data.rb +20 -0
- data/examples/area.rb +103 -0
- data/examples/bar_column_chart.rb +55 -0
- data/examples/barley/barley.rb +29 -19
- data/examples/barley/barley_data.rb +122 -0
- data/examples/crimea/crimea_grouped_bar.rb +59 -0
- data/examples/dot.rb +19 -0
- data/examples/first.rb +1 -6
- data/examples/line.rb +84 -0
- data/examples/pie_and_donut.rb +38 -0
- data/examples/scatterplot.rb +55 -0
- data/examples/second.rb +3 -4
- data/examples/third.rb +1 -1
- data/lib/rubyvis.rb +3 -1
- data/lib/rubyvis/color/color.rb +31 -3
- data/lib/rubyvis/color/colors.rb +3 -3
- data/lib/rubyvis/format/number.rb +80 -10
- data/lib/rubyvis/internals.rb +11 -5
- data/lib/rubyvis/javascript_behaviour.rb +1 -0
- data/lib/rubyvis/mark.rb +43 -20
- data/lib/rubyvis/mark/anchor.rb +1 -1
- data/lib/rubyvis/mark/area.rb +15 -13
- data/lib/rubyvis/mark/bar.rb +2 -2
- data/lib/rubyvis/mark/dot.rb +85 -0
- data/lib/rubyvis/mark/label.rb +1 -1
- data/lib/rubyvis/mark/line.rb +7 -6
- data/lib/rubyvis/mark/panel.rb +0 -1
- data/lib/rubyvis/mark/rule.rb +5 -4
- data/lib/rubyvis/mark/wedge.rb +124 -0
- data/lib/rubyvis/nest.rb +158 -0
- data/lib/rubyvis/scale.rb +4 -0
- data/lib/rubyvis/scale/log.rb +55 -0
- data/lib/rubyvis/scale/ordinal.rb +34 -11
- data/lib/rubyvis/scale/quantitative.rb +17 -3
- data/lib/rubyvis/scene/svg_area.rb +197 -0
- data/lib/rubyvis/scene/svg_dot.rb +67 -0
- data/lib/rubyvis/scene/svg_label.rb +17 -15
- data/lib/rubyvis/scene/svg_line.rb +0 -2
- data/lib/rubyvis/scene/svg_rule.rb +2 -2
- data/lib/rubyvis/scene/svg_scene.rb +8 -1
- data/lib/rubyvis/scene/svg_wedge.rb +56 -0
- data/lib/rubyvis/sceneelement.rb +2 -1
- data/spec/bar_spec.rb +27 -3
- data/spec/label_spec.rb +1 -1
- data/spec/nest_spec.rb +41 -0
- data/spec/panel_spec.rb +1 -1
- data/spec/scale_linear_spec.rb +2 -2
- data/spec/scale_ordinal_spec.rb +81 -0
- data/spec/spec.opts +0 -1
- metadata +24 -3
- metadata.gz.sig +0 -0
data/lib/rubyvis/mark/bar.rb
CHANGED
@@ -8,9 +8,9 @@ module Rubyvis
|
|
8
8
|
end
|
9
9
|
|
10
10
|
@properties=Mark.properties.dup
|
11
|
-
attr_accessor_dsl :width, :height, :line_width, :stroke_style, :fill_style
|
11
|
+
attr_accessor_dsl :width, :height, :line_width, [:stroke_style, lambda {|d| pv.color(d)}], [:fill_style, lambda {|d| pv.color(d)}]
|
12
12
|
def self.defaults
|
13
|
-
Bar.new.extend(Mark.defaults).line_width(1.5).fill_style(Rubyvis.Colors.category20().
|
13
|
+
Bar.new.extend(Mark.defaults).line_width(1.5).fill_style( lambda {Rubyvis.Colors.category20().scale(self.parent.index)})
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Rubyvis
|
2
|
+
def self.Dot
|
3
|
+
Rubyvis::Dot
|
4
|
+
end
|
5
|
+
class Dot < Mark
|
6
|
+
def type
|
7
|
+
"dot"
|
8
|
+
end
|
9
|
+
|
10
|
+
@properties=Mark.properties.dup
|
11
|
+
|
12
|
+
attr_accessor_dsl :shape, :shape_angle, :shape_radius, :shape_size, :line_width, [:stroke_style, lambda {|d| pv.color(d)}], [:fill_style, lambda {|d| pv.color(d)}]
|
13
|
+
|
14
|
+
def self.defaults()
|
15
|
+
Dot.new().extend(Mark.defaults).shape("circle"). line_width(1.5). stroke_style(lambda {pv.Colors.category10().scale(self.parent.index)})
|
16
|
+
end
|
17
|
+
|
18
|
+
def anchor(name)
|
19
|
+
|
20
|
+
|
21
|
+
mark_anchor(name).left(lambda {
|
22
|
+
s=scene.target[self.index]
|
23
|
+
case self.name
|
24
|
+
when 'bottom' then s.left;
|
25
|
+
when 'top' then s.left;
|
26
|
+
when 'center' then s.left;
|
27
|
+
when 'left' then nil;
|
28
|
+
else
|
29
|
+
s.left+s.shape_radius
|
30
|
+
end
|
31
|
+
}).right(lambda {
|
32
|
+
s=scene.target[self.index]
|
33
|
+
self.name()=='left' ? s.right+s.shape_radius : nil
|
34
|
+
|
35
|
+
}).top(lambda {
|
36
|
+
s=scene.target[self.index]
|
37
|
+
case self.name
|
38
|
+
when 'left' then s.top;
|
39
|
+
when 'right' then s.top;
|
40
|
+
when 'center' then s.top;
|
41
|
+
when 'top' then nil;
|
42
|
+
else
|
43
|
+
s.top+s.shape_radius
|
44
|
+
end
|
45
|
+
}).bottom(lambda {
|
46
|
+
s=scene.target[self.index]
|
47
|
+
self.name()=='top' ? s.bottom+s.shape_radius : nil
|
48
|
+
}).text_align(lambda {
|
49
|
+
case self.name
|
50
|
+
when 'left' then 'right';
|
51
|
+
when 'bottom' then 'center';
|
52
|
+
when 'top' then 'center';
|
53
|
+
when 'center' then 'center';
|
54
|
+
else
|
55
|
+
'left'
|
56
|
+
end
|
57
|
+
}).text_baseline( lambda {
|
58
|
+
case self.name
|
59
|
+
when 'right' then 'middle';
|
60
|
+
when 'left' then 'middle';
|
61
|
+
when 'center' then 'middle';
|
62
|
+
when 'bottom' then 'top';
|
63
|
+
else
|
64
|
+
'bottom'
|
65
|
+
end
|
66
|
+
|
67
|
+
})
|
68
|
+
end
|
69
|
+
def build_implied(s)
|
70
|
+
r = s.shape_radius
|
71
|
+
z = s.shape_size
|
72
|
+
if (r.nil?)
|
73
|
+
if (z.nil?)
|
74
|
+
s.shape_size = 20.25;
|
75
|
+
s.shape_radius = 4.5;
|
76
|
+
else
|
77
|
+
s.shape_radius = Math.sqrt(z)
|
78
|
+
end
|
79
|
+
elsif (z.nil?)
|
80
|
+
s.shape_size = r * r;
|
81
|
+
end
|
82
|
+
mark_build_implied(s)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/rubyvis/mark/label.rb
CHANGED
@@ -5,7 +5,7 @@ module Rubyvis
|
|
5
5
|
|
6
6
|
class Label < Mark
|
7
7
|
@properties=Mark.properties.dup
|
8
|
-
attr_accessor_dsl :text, :font, :text_angle, :text_style, :text_align, :text_baseline, :text_margin, :text_decoration, :text_shadow
|
8
|
+
attr_accessor_dsl :text, :font, :text_angle, [:text_style, lambda {|d| pv.color(d)}], :text_align, :text_baseline, :text_margin, :text_decoration, :text_shadow
|
9
9
|
def type
|
10
10
|
'label'
|
11
11
|
end
|
data/lib/rubyvis/mark/line.rb
CHANGED
@@ -5,18 +5,19 @@ module Rubyvis
|
|
5
5
|
module LinePrototype
|
6
6
|
include AreaPrototype
|
7
7
|
def line_anchor(name)
|
8
|
-
area_anchor(name).text_align(lambda {|d|
|
9
|
-
|
8
|
+
anchor=area_anchor(name).text_align(lambda {|d|
|
9
|
+
{'left'=>'right','bottom'=>'center', 'top'=>'center','center'=>'center','right'=>'left'}[self.name]
|
10
10
|
}).text_baseline(lambda{|d|
|
11
|
-
{'top'=>'bottom','right'=>'middle', 'left'=>'middle','center'=>'middle','bottom'=>'top'}[
|
11
|
+
{'top'=>'bottom', 'right'=>'middle', 'left'=>'middle','center'=>'middle','bottom'=>'top'}[self.name]
|
12
12
|
})
|
13
|
+
return anchor
|
13
14
|
end
|
14
15
|
end
|
15
16
|
class Line < Mark
|
16
17
|
include AreaPrototype
|
17
18
|
include LinePrototype
|
18
19
|
@properties=Mark.properties.dup
|
19
|
-
attr_accessor_dsl :line_width, :line_join, :stroke_style, :fill_style, :segmented, :interpolate, :eccentricity, :tension
|
20
|
+
attr_accessor_dsl :line_width, :line_join, [:stroke_style, lambda {|d| pv.color(d)}], [:fill_style, lambda {|d| pv.color(d)}], :segmented, :interpolate, :eccentricity, :tension
|
20
21
|
def type
|
21
22
|
"line"
|
22
23
|
end
|
@@ -29,8 +30,8 @@ module Rubyvis
|
|
29
30
|
def build_instance(*args)
|
30
31
|
area_build_instance(*args)
|
31
32
|
end
|
32
|
-
def defaults
|
33
|
-
Line.new.extend(
|
33
|
+
def self.defaults
|
34
|
+
Line.new.extend(Mark.defaults).line_join('miter').line_width(1.5).stroke_style( lambda {return Rubyvis::Colors.category10().scale(self.parent.index)}).interpolate('linear').eccentricity(0).tension(7)
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
data/lib/rubyvis/mark/panel.rb
CHANGED
data/lib/rubyvis/mark/rule.rb
CHANGED
@@ -5,9 +5,9 @@ module Rubyvis
|
|
5
5
|
class Rule < Mark
|
6
6
|
include LinePrototype
|
7
7
|
@properties=Mark.properties.dup
|
8
|
-
attr_accessor_dsl :width, :height, :line_width, :stroke_style
|
8
|
+
attr_accessor_dsl :width, :height, :line_width, [:stroke_style, lambda {|d| pv.color(d)}]
|
9
9
|
def self.defaults
|
10
|
-
Rule.new.extend(Mark.defaults).line_width(1).stroke_style(
|
10
|
+
Rule.new.extend(Mark.defaults).line_width(1).stroke_style('black').antialias(false)
|
11
11
|
end
|
12
12
|
def type
|
13
13
|
'rule'
|
@@ -21,12 +21,13 @@ module Rubyvis
|
|
21
21
|
r=s.right
|
22
22
|
t=s.top
|
23
23
|
b=s.bottom
|
24
|
-
|
24
|
+
|
25
|
+
if((!s.width.nil?) or ((l.nil?) and (r.nil?)) or ((!r.nil?) and (!l.nil?)))
|
25
26
|
s.height=0
|
26
27
|
else
|
27
28
|
s.width=0
|
28
29
|
end
|
29
|
-
|
30
|
+
mark_build_implied(s)
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Rubyvis
|
2
|
+
def self.Wedge
|
3
|
+
Rubyvis::Wedge
|
4
|
+
end
|
5
|
+
class Wedge < Mark
|
6
|
+
def type
|
7
|
+
"wedge"
|
8
|
+
end
|
9
|
+
@properties=Mark.properties.dup
|
10
|
+
attr_accessor_dsl :start_angle, :end_angle, :angle, :inner_radius, :outer_radius, :line_width, [:stroke_style, lambda {|d| pv.color(d)}], [:fill_style, lambda {|d| pv.color(d)}]
|
11
|
+
def self.defaults
|
12
|
+
Wedge.new.extend(Mark.defaults).start_angle(lambda {s=self.sibling; s ? s.end_angle: -Math::PI.quo(2) } ).inner_radius( 0 ).line_width( 1.5 ).stroke_style( nil ).fill_style( lambda {Rubyvis.Colors.category20().scale(self.parent.index)})
|
13
|
+
end
|
14
|
+
def mid_radius
|
15
|
+
(inner_radius+outer_radius) / 2.0
|
16
|
+
end
|
17
|
+
def mid_angle
|
18
|
+
(start_angle+end_angle) / 2.0
|
19
|
+
end
|
20
|
+
|
21
|
+
def anchor(name)
|
22
|
+
partial=lambda {|s| s.inner_radius!=0 ? true : s.angle<2*Math.PI}
|
23
|
+
mid_radius=lambda {|s| (s.inner_radius+s.outer_radius) / 2.0}
|
24
|
+
mid_angle=lambda {|s| (s.start_angle+s.end_angle) / 2.0 }
|
25
|
+
|
26
|
+
mark_anchor(name).left(lambda {
|
27
|
+
s = self.scene.target[self.index];
|
28
|
+
if (partial.call(s))
|
29
|
+
|
30
|
+
case (self.name())
|
31
|
+
when "outer"
|
32
|
+
return s.left + s.outer_radius * Math.cos(mid_angle.call(s))
|
33
|
+
when "inner"
|
34
|
+
return s.left + s.inner_radius * Math.cos(mid_angle.call(s))
|
35
|
+
when "start"
|
36
|
+
return s.left + mid_radius.call(s) * Math.cos(s.start_angle)
|
37
|
+
when "center"
|
38
|
+
return s.left + mid_radius.call(s) * Math.cos(mid_angle.call(s))
|
39
|
+
when "end"
|
40
|
+
return s.left + mid_radius.call(s) * Math.cos(s.end_angle)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
return s.left;
|
44
|
+
}).top(lambda {
|
45
|
+
s = self.scene.target[self.index];
|
46
|
+
if (partial.call(s))
|
47
|
+
case (self.name())
|
48
|
+
when "outer"
|
49
|
+
return s.top + s.outer_radius * Math.sin(mid_angle.call(s))
|
50
|
+
when "inner"
|
51
|
+
return s.top + s.inner_radius * Math.sin(mid_angle.call(s))
|
52
|
+
when "start"
|
53
|
+
return s.top + mid_radius.call(s) * Math.sin(s.start_angle)
|
54
|
+
when "center"
|
55
|
+
return s.top + mid_radius.call(s) * Math.sin(mid_angle.call(s))
|
56
|
+
when "end"
|
57
|
+
return s.top + mid_radius.call(s) * Math.sin(s.end_angle)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
return s.top;
|
61
|
+
|
62
|
+
}).text_align(lambda {
|
63
|
+
s = self.scene.target[self.index];
|
64
|
+
if (partial.call(s))
|
65
|
+
case (self.name())
|
66
|
+
when 'outer'
|
67
|
+
return self.upright(mid_angle.call(s)) ? 'right':'left'
|
68
|
+
when 'inner'
|
69
|
+
return self.upright(mid_angle.call(s)) ? 'left':'right'
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
return 'center'
|
74
|
+
}).text_baseline(lambda {
|
75
|
+
s = self.scene.target[self.index];
|
76
|
+
if (partial.call(s))
|
77
|
+
case (self.name())
|
78
|
+
when 'start'
|
79
|
+
return self.upright(s.start_angle) ? 'top':'bottom'
|
80
|
+
when 'end'
|
81
|
+
return self.upright(s.end_angle) ? 'bottom':'top'
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
return 'middle'
|
86
|
+
}).text_angle(lambda {
|
87
|
+
s = self.scene.target[self.index];
|
88
|
+
a=0
|
89
|
+
if (partial.call(s))
|
90
|
+
case (self.name())
|
91
|
+
when 'center'
|
92
|
+
a=mid_angle.call(s)
|
93
|
+
when 'inner'
|
94
|
+
a=mid_angle.call(s)
|
95
|
+
when 'outer'
|
96
|
+
a=mid_angle.call(s)
|
97
|
+
when 'start'
|
98
|
+
a=s.start_angle
|
99
|
+
when 'end'
|
100
|
+
a=s.end_angle
|
101
|
+
end
|
102
|
+
end
|
103
|
+
self.upright(a) ? a: (a+Math::PI)
|
104
|
+
})
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def self.upright(angle)
|
110
|
+
angle=angle % (2*Math::PI)
|
111
|
+
angle=(angle<0) ? (2*Math::PI+angle) : angle
|
112
|
+
(angle < Math::PI/2.0) or (angle>=3*Math::PI / 2.0)
|
113
|
+
|
114
|
+
end
|
115
|
+
def build_implied(s)
|
116
|
+
if (s.angle.nil?)
|
117
|
+
s.angle= s.end_angle-s.start_angle
|
118
|
+
elsif s.end_angle.nil?
|
119
|
+
s.end_angle=s.start_angle+s.angle
|
120
|
+
end
|
121
|
+
mark_build_implied(s)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/rubyvis/nest.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
module Rubyvis
|
2
|
+
##
|
3
|
+
# Returns a {@link pv.Nest} operator for the specified array. This is a
|
4
|
+
# convenience factory method, equivalent to <tt>new pv.Nest(array)</tt>.
|
5
|
+
#
|
6
|
+
# @see pv.Nest
|
7
|
+
# @param {array} array an array of elements to nest.
|
8
|
+
# @returns {Nest} a nest operator for the specified array.
|
9
|
+
##
|
10
|
+
def self.nest(array)
|
11
|
+
Nest.new(array)
|
12
|
+
end
|
13
|
+
class NestedArray
|
14
|
+
attr_accessor :key, :values
|
15
|
+
def initialize(opts)
|
16
|
+
@key=opts[:key]
|
17
|
+
@values=opts[:values]
|
18
|
+
end
|
19
|
+
def ==(var)
|
20
|
+
key==var.key and values=var.values
|
21
|
+
end
|
22
|
+
end
|
23
|
+
##
|
24
|
+
# Constructs a nest operator for the specified array. This constructor should
|
25
|
+
# not be invoked directly; use {@link pv.nest} instead.
|
26
|
+
#
|
27
|
+
# @class Represents a {@link Nest} operator for the specified array. Nesting
|
28
|
+
# allows elements in an array to be grouped into a hierarchical tree
|
29
|
+
# structure. The levels in the tree are specified by <i>key</i> functions. The
|
30
|
+
# leaf nodes of the tree can be sorted by value, while the internal nodes can
|
31
|
+
# be sorted by key. Finally, the tree can be returned either has a
|
32
|
+
# multidimensional array via {@link #entries}, or as a hierarchical map via
|
33
|
+
# {@link #map}. The {@link #rollup} routine similarly returns a map, collapsing
|
34
|
+
# the elements in each leaf node using a summary function.
|
35
|
+
#
|
36
|
+
# <p>For example, consider the following tabular data structure of Barley
|
37
|
+
# yields, from various sites in Minnesota during 1931-2:
|
38
|
+
#
|
39
|
+
# { yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
|
40
|
+
# { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
|
41
|
+
# { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" }
|
42
|
+
#
|
43
|
+
# To facilitate visualization, it may be useful to nest the elements first by
|
44
|
+
# year, and then by variety, as follows:
|
45
|
+
#
|
46
|
+
# <pre>var nest = pv.nest(yields)
|
47
|
+
# .key(function(d) d.year)
|
48
|
+
# .key(function(d) d.variety)
|
49
|
+
# .entries();</pre>
|
50
|
+
#
|
51
|
+
# This returns a nested array. Each element of the outer array is a key-values
|
52
|
+
# pair, listing the values for each distinct key:
|
53
|
+
#
|
54
|
+
# <pre>{ key: 1931, values: [
|
55
|
+
# { key: "Manchuria", values: [
|
56
|
+
# { yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
|
57
|
+
# { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
|
58
|
+
# { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" },
|
59
|
+
# ...
|
60
|
+
# ] },
|
61
|
+
# { key: "Glabron", values: [
|
62
|
+
# { yield: 43.07, variety: "Glabron", year: 1931, site: "University Farm" },
|
63
|
+
# { yield: 55.20, variety: "Glabron", year: 1931, site: "Waseca" },
|
64
|
+
# ...
|
65
|
+
# ] },
|
66
|
+
# ] },
|
67
|
+
# { key: 1932, values: ... }</pre>
|
68
|
+
#
|
69
|
+
# Further details, including sorting and rollup, is provided below on the
|
70
|
+
# corresponding methods.
|
71
|
+
#
|
72
|
+
# @param {array} array an array of elements to nest.
|
73
|
+
#/
|
74
|
+
class Nest
|
75
|
+
attr_accessor :array, :keys, :order
|
76
|
+
def initialize(array)
|
77
|
+
@array=array
|
78
|
+
@keys=[]
|
79
|
+
@order=nil
|
80
|
+
end
|
81
|
+
def key(k)
|
82
|
+
@keys.push(k)
|
83
|
+
return self
|
84
|
+
end
|
85
|
+
def sort_keys(order=nil)
|
86
|
+
#keys[keys.size-1].order = order.nil? ? pv.natural_order : order
|
87
|
+
return self
|
88
|
+
end
|
89
|
+
def sort_values(order=nil)
|
90
|
+
@order = order.nil? ? pv.natural_order : order
|
91
|
+
return self
|
92
|
+
end
|
93
|
+
def map
|
94
|
+
i=0
|
95
|
+
map={}
|
96
|
+
values=[]
|
97
|
+
@array.each_with_index {|x,j|
|
98
|
+
m=map
|
99
|
+
(@keys.size-1).times {|i|
|
100
|
+
k=@keys[i].call(x)
|
101
|
+
m[k]={} if (!m[k])
|
102
|
+
m=m[k]
|
103
|
+
}
|
104
|
+
k=@keys.last.call(x)
|
105
|
+
if(!m[k])
|
106
|
+
a=[]
|
107
|
+
values.push(a)
|
108
|
+
m[k]=a
|
109
|
+
end
|
110
|
+
m[k].push(x)
|
111
|
+
}
|
112
|
+
if(self.order)
|
113
|
+
values.each_with_index {|v,vi|
|
114
|
+
values[vi].sort(&self.order)
|
115
|
+
}
|
116
|
+
end
|
117
|
+
map
|
118
|
+
end
|
119
|
+
def entries()
|
120
|
+
entries_sort(entries_entries(map),0)
|
121
|
+
end
|
122
|
+
def entries_entries(map)
|
123
|
+
array=[]
|
124
|
+
map.each_pair {|k,v|
|
125
|
+
array.push(NestedArray.new({:key=>k, :values=>(v.is_a? Array) ? v: entries_entries(v)}))
|
126
|
+
}
|
127
|
+
array
|
128
|
+
end
|
129
|
+
def entries_sort(array,i)
|
130
|
+
o=keys[i].order
|
131
|
+
if o
|
132
|
+
array.sort {|a,b| o(a.key, b.key)}
|
133
|
+
end
|
134
|
+
i+=1
|
135
|
+
if (i<keys.size)
|
136
|
+
array.each {|v|
|
137
|
+
entries_sort(v, i)
|
138
|
+
}
|
139
|
+
|
140
|
+
end
|
141
|
+
return array
|
142
|
+
|
143
|
+
end
|
144
|
+
def rollup_rollup(map,f)
|
145
|
+
map.each_pair {|key,value|
|
146
|
+
if value.is_a? Array
|
147
|
+
map[key]=f.call(value)
|
148
|
+
else
|
149
|
+
rollup_rollup(value)
|
150
|
+
end
|
151
|
+
}
|
152
|
+
return map;
|
153
|
+
end
|
154
|
+
def rollup(f)
|
155
|
+
rollup_rollup(self.map,f)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|