rubyvis 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|