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