1 /**
  2  * Returns a {@link pv.Tree} operator for the specified array. This is a
  3  * convenience factory method, equivalent to <tt>new pv.Tree(array)</tt>.
  4  *
  5  * @see pv.Tree
  6  * @param {array} array an array from which to construct a tree.
  7  * @returns {pv.Tree} a tree operator for the specified array.
  8  */
  9 pv.tree = function(array) {
 10   return new pv.Tree(array);
 11 };
 12 
 13 /**
 14  * Constructs a tree operator for the specified array. This constructor should
 15  * not be invoked directly; use {@link pv.tree} instead.
 16  *
 17  * @class Represents a tree operator for the specified array. The tree operator
 18  * allows a hierarchical map to be constructed from an array; it is similar to
 19  * the {@link pv.Nest} operator, except the hierarchy is derived dynamically
 20  * from the array elements.
 21  *
 22  * <p>For example, given an array of size information for ActionScript classes:
 23  *
 24  * <pre>{ name: "flare.flex.FlareVis", size: 4116 },
 25  * { name: "flare.physics.DragForce", size: 1082 },
 26  * { name: "flare.physics.GravityForce", size: 1336 }, ...</pre>
 27  *
 28  * To facilitate visualization, it may be useful to nest the elements by their
 29  * package hierarchy:
 30  *
 31  * <pre>var tree = pv.tree(classes)
 32  *     .keys(function(d) d.name.split("."))
 33  *     .map();</pre>
 34  *
 35  * The resulting tree is:
 36  *
 37  * <pre>{ flare: {
 38  *     flex: {
 39  *       FlareVis: {
 40  *         name: "flare.flex.FlareVis",
 41  *         size: 4116 } },
 42  *     physics: {
 43  *       DragForce: {
 44  *         name: "flare.physics.DragForce",
 45  *         size: 1082 },
 46  *       GravityForce: {
 47  *         name: "flare.physics.GravityForce",
 48  *         size: 1336 } },
 49  *     ... } }</pre>
 50  *
 51  * By specifying a value function,
 52  *
 53  * <pre>var tree = pv.tree(classes)
 54  *     .keys(function(d) d.name.split("."))
 55  *     .value(function(d) d.size)
 56  *     .map();</pre>
 57  *
 58  * we can further eliminate redundant data:
 59  *
 60  * <pre>{ flare: {
 61  *     flex: {
 62  *       FlareVis: 4116 },
 63  *     physics: {
 64  *       DragForce: 1082,
 65  *       GravityForce: 1336 },
 66  *   ... } }</pre>
 67  *
 68  * For visualizations with large data sets, performance improvements may be seen
 69  * by storing the data in a tree format, and then flattening it into an array at
 70  * runtime with {@link pv.Flatten}.
 71  *
 72  * @param {array} array an array from which to construct a tree.
 73  */
 74 pv.Tree = function(array) {
 75   this.array = array;
 76 };
 77 
 78 /**
 79  * Assigns a <i>keys</i> function to this operator; required. The keys function
 80  * returns an array of <tt>string</tt>s for each element in the associated
 81  * array; these keys determine how the elements are nested in the tree. The
 82  * returned keys should be unique for each element in the array; otherwise, the
 83  * behavior of this operator is undefined.
 84  *
 85  * @param {function} k the keys function.
 86  * @returns {pv.Tree} this.
 87  */
 88 pv.Tree.prototype.keys = function(k) {
 89   this.k = k;
 90   return this;
 91 };
 92 
 93 /**
 94  * Assigns a <i>value</i> function to this operator; optional. The value
 95  * function specifies an optional transformation of the element in the array
 96  * before it is inserted into the map. If no value function is specified, it is
 97  * equivalent to using the identity function.
 98  *
 99  * @param {function} k the value function.
100  * @returns {pv.Tree} this.
101  */
102 pv.Tree.prototype.value = function(v) {
103   this.v = v;
104   return this;
105 };
106 
107 /**
108  * Returns a hierarchical map of values. The hierarchy is determined by the keys
109  * function; the values in the map are determined by the value function.
110  *
111  * @returns a hierarchical map of values.
112  */
113 pv.Tree.prototype.map = function() {
114   var map = {}, o = {};
115   for (var i = 0; i < this.array.length; i++) {
116     o.index = i;
117     var value = this.array[i], keys = this.k.call(o, value), node = map;
118     for (var j = 0; j < keys.length - 1; j++) {
119       node = node[keys[j]] || (node[keys[j]] = {});
120     }
121     node[keys[j]] = this.v ? this.v.call(o, value) : value;
122   }
123   return map;
124 };
125