| 
									
										
										
										
											2010-11-09 10:44:25 -07:00
										 |  |  | // TODO beef up AHR to allow streamed parsing
 | 
					
						
							| 
									
										
										
										
											2010-11-09 01:30:44 -07:00
										 |  |  | "use strict"; | 
					
						
							|  |  |  | (function (undefined) { | 
					
						
							|  |  |  |   var fs = require('fs'), | 
					
						
							| 
									
										
										
										
											2010-11-29 00:44:27 -07:00
										 |  |  |     request = require('ahr'), | 
					
						
							| 
									
										
										
										
											2010-11-09 01:30:44 -07:00
										 |  |  |     stream, | 
					
						
							|  |  |  |     node = process.argv[0], | 
					
						
							|  |  |  |     self = process.argv[1], | 
					
						
							| 
									
										
										
										
											2010-11-29 00:44:27 -07:00
										 |  |  |     fulluri = process.argv[2]; | 
					
						
							| 
									
										
										
										
											2010-11-09 01:30:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   self = self.substr(self.lastIndexOf('/') + 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   require('remedial'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function usage() { | 
					
						
							|  |  |  |     console.log("Usage: node {self}".supplant({self: self}) +  | 
					
						
							|  |  |  |       " file:///path/to/file | http://path/to/file"); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Head, and Dependent "Classes"
 | 
					
						
							|  |  |  |   function Comment(lines) { | 
					
						
							|  |  |  |     while (true) { | 
					
						
							|  |  |  |       line = lines.shift(); | 
					
						
							|  |  |  |       if (undefined === line || null === line) { | 
					
						
							|  |  |  |         throw new Error("Expected 'Dependent' or 'end' but reach end-of-file"); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (!line.match(/\s*#/)){ | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return line; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function Dependent(lines) { | 
					
						
							|  |  |  |     var line, dep; | 
					
						
							|  |  |  |     line = Comment(lines); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     if (line.match(/^Dependent\s*/)) { | 
					
						
							|  |  |  |       parts = line.split(/\s+/); | 
					
						
							|  |  |  |       dep = { | 
					
						
							|  |  |  |         // no error checking in spec
 | 
					
						
							|  |  |  |         name: parts[1], | 
					
						
							|  |  |  |         gender: parts[2], | 
					
						
							|  |  |  |         age: parts[3], | 
					
						
							|  |  |  |         height: parseInt(parts[4], 10), | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     } else if (line.match(/^End\s*/)) { | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       throw new Error("Expected 'Dependent' or 'End' but saw\n    " + line.substr(0,20)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return dep; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function Head(lines) { | 
					
						
							|  |  |  |     var line, head, parts, dep; | 
					
						
							|  |  |  |     line = Comment(lines); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (line.match(/^Head\s*/)) { | 
					
						
							|  |  |  |       parts = line.split(/\s+/); | 
					
						
							|  |  |  |       head = { | 
					
						
							|  |  |  |         // no error checking in spec
 | 
					
						
							|  |  |  |         name: parts[1], | 
					
						
							|  |  |  |         gender: parts[2], | 
					
						
							|  |  |  |         eye_color: parts[3], | 
					
						
							|  |  |  |         hair_color: parts[4], | 
					
						
							|  |  |  |         height: parseInt(parts[5], 10), | 
					
						
							|  |  |  |         deps: [] | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       while (dep = Dependent(lines)) { | 
					
						
							|  |  |  |         head.deps.push(dep); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else if (line.match(/^SUPER-END\s*/)) { | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         throw new Error("Expected 'Head' or 'SUPER-END' but saw\n    " + line.substr(0,20)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return head; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Queries
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Average height of Heads of Household
 | 
					
						
							|  |  |  |   function query1(heads) { | 
					
						
							|  |  |  |     var heights = 0.0; | 
					
						
							|  |  |  |     heads.forEach(function (head) { | 
					
						
							|  |  |  |       heights += head.height; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     console.log("Average Height of Heads of Household: " + (heights / heads.length)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // What is the height of the tallest of all dependents?
 | 
					
						
							|  |  |  |   function query2(heads) { | 
					
						
							|  |  |  |     var max = 0.0; | 
					
						
							|  |  |  |     heads.forEach(function (head) { | 
					
						
							|  |  |  |       head.deps.forEach(function (dep) { | 
					
						
							|  |  |  |         max = Math.max(max, dep.height); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     console.log("Height of tallest Dependent: " + max); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // The height of the shortest Dependent of a female Head of Household
 | 
					
						
							|  |  |  |   function query3(heads) { | 
					
						
							|  |  |  |     var min = Infinity; | 
					
						
							|  |  |  |     heads.forEach(function (head) { | 
					
						
							|  |  |  |       if ('F' !== head.gender) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       head.deps.forEach(function (dep) { | 
					
						
							|  |  |  |         min = Math.min(min, dep.height); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     console.log("Height of shortest Dependent of a Female-Headed Household: " + min); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Average height of male dependents of brown-haired male heads of houshold
 | 
					
						
							|  |  |  |   function query4(heads) { | 
					
						
							|  |  |  |     var heights = 0.0, count = 0; | 
					
						
							|  |  |  |     heads.forEach(function (head) { | 
					
						
							|  |  |  |       if ('m' !== head.gender && 'brown' !== head.hair_color) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       head.deps.forEach(function (dep) { | 
					
						
							|  |  |  |         count += 1; | 
					
						
							|  |  |  |         heights += dep.height; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     console.log("Average height of male dependent of brown-haired male Heads of Household: " + (heights / count)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-09 10:44:25 -07:00
										 |  |  |   function parseChunk(chunk) { | 
					
						
							|  |  |  |     /* | 
					
						
							|  |  |  |     // TODO be more memory efficient
 | 
					
						
							|  |  |  |     // by seeking into the buffer
 | 
					
						
							|  |  |  |     data += chunk | 
					
						
							|  |  |  |     while (true) { | 
					
						
							|  |  |  |       index = data.indexOf('\n') | 
					
						
							|  |  |  |       if (-1 === index) { | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       line = data.substr(0, index); | 
					
						
							|  |  |  |       data = data.substr(index + 1); | 
					
						
							|  |  |  |       parseLine(line); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     */ | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-29 00:44:27 -07:00
										 |  |  |   var data = ''; | 
					
						
							|  |  |  |   function parseData(err, x, chunk, end) { | 
					
						
							| 
									
										
										
										
											2010-11-29 00:50:13 -07:00
										 |  |  |     if (err) { | 
					
						
							|  |  |  |       usage(); | 
					
						
							|  |  |  |       throw err; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-11-29 00:44:27 -07:00
										 |  |  |     if (!end) { | 
					
						
							|  |  |  |       data += chunk.toString(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-11-09 01:30:44 -07:00
										 |  |  |     var lines = data.split('\n'), | 
					
						
							|  |  |  |       heads = [], | 
					
						
							|  |  |  |       curHead; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (curHead = Head(lines)) { | 
					
						
							|  |  |  |       heads.push(curHead); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     console.log(JSON.stringify(heads, null, '  ')); | 
					
						
							|  |  |  |     query1(heads); | 
					
						
							|  |  |  |     query2(heads); | 
					
						
							|  |  |  |     query3(heads); | 
					
						
							|  |  |  |     query4(heads); | 
					
						
							|  |  |  |     return heads; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function main() { | 
					
						
							|  |  |  |     if (!fulluri) { | 
					
						
							|  |  |  |       usage(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2010-11-29 00:50:13 -07:00
										 |  |  |     request.get(fulluri, undefined, { stream: true }).when(parseData); | 
					
						
							| 
									
										
										
										
											2010-11-09 01:30:44 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   main(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }()); |