1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
module Rubyvis
# Returns a {@link pv.Flatten} operator for the specified map. This is a
# convenience factory method, equivalent to <tt>new pv.Flatten(map)</tt>.
def self.flatten(map)
Flatten.new(map)
end
# Represents a flatten operator for the specified array. Flattening
# allows hierarchical maps to be flattened into an array. The levels in the
# input tree are specified by <i>key</i> functions.
#
# <p>For example, consider the following hierarchical data structure of Barley
# yields, from various sites in Minnesota during 1931-2:
#
# <pre>{ 1931: {
# Manchuria: {
# "University Farm": 27.00,
# "Waseca": 48.87,
# "Morris": 27.43,
# ... },
# Glabron: {
# "University Farm": 43.07,
# "Waseca": 55.20,
# ... } },
# 1932: {
# ... } }</pre>
#
# To facilitate visualization, it may be useful to flatten the tree into a
# tabular array:
#
# <pre>var array = pv.flatten(yields)
# .key("year")
# .key("variety")
# .key("site")
# .key("yield")
# .array();</pre>
#
# This returns an array of object elements. Each element in the array has
# attributes corresponding to this flatten operator's keys:
#
# <pre>{ site: "University Farm", variety: "Manchuria", year: 1931, yield: 27 },
# { site: "Waseca", variety: "Manchuria", year: 1931, yield: 48.87 },
# { site: "Morris", variety: "Manchuria", year: 1931, yield: 27.43 },
# { site: "University Farm", variety: "Glabron", year: 1931, yield: 43.07 },
# { site: "Waseca", variety: "Glabron", year: 1931, yield: 55.2 }, ...</pre>
#
# <p>The flatten operator is roughly the inverse of the {@link pv.Nest} and
# {@link pv.Tree} operators.
#
class Flatten
def initialize(map)
@map=map
@keys=[]
@leaf=nil
end
# Flattens using the specified key function. Multiple keys may be added to the
# flatten; the tiers of the underlying tree must correspond to the specified
# keys, in order. The order of the returned array is undefined; however, you
# can easily sort it.
#
# @param {string} key the key name.
# @param {function} [f] an optional value map function.
# @returns {pv.Nest} this.
def key(k, f=nil)
@keys.push(OpenStruct.new({:name=>k, :value=>f}))
@leaf=nil
self
end
# Flattens using the specified leaf function. This is an alternative to
# specifying an explicit set of keys; the tiers of the underlying tree
# will be determined dynamically by recursing on the values, and the resulting keys will be stored in the entries +:keys+ attribute. The leaf function must return true for leaves, and false for internal nodes.
#
# @param {function} f a leaf function.
# @returns {pv.Nest} this.
def leaf(f)
@keys.clear
@leaf=f
self
end
def recurse(value,i)
if @leaf.call(value)
@entries.push({:keys=>@stack.dup, :value=>value})
else
value.each {|key,v|
@stack.push(key)
recurse(v, i+1)
@stack.pop
}
end
end
def visit(value,i)
if (i < @keys.size - 1)
value.each {|key,v|
@stack.push(key)
visit(v,i+1)
@stack.pop
}
else
@entries.push(@stack+[value])
end
end
# Returns the flattened array. Each entry in the array is an object; each
# object has attributes corresponding to this flatten operator's keys.
#
# @returns an array of elements from the flattened map.
def array
@entries=[]
@stack=[]
if @leaf
recurse(@map,0)
return @entries
end
visit(@map,0)
@entries.map {|stack|
m={}
@keys.each_with_index {|k,i|
v=stack[i]
m[k.name]=k.value ? k.value.js_call(self,v) : v
}
m
}
end
alias :to_a :array
end
end
|