Skip to content

Instantly share code, notes, and snippets.

@fhdhsni
Created August 7, 2016 19:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fhdhsni/c48414bf1df27b2ebabf7a1021744cb3 to your computer and use it in GitHub Desktop.
Save fhdhsni/c48414bf1df27b2ebabf7a1021744cb3 to your computer and use it in GitHub Desktop.
// Secret life of JS objects
"use strict";
(() => {
const MOUNTAINS = require("./mountains");
function rowHeights(rows) {
// We have 8 row(Three are 8 array in rows). We wanna find out how many line(height) each row needs. Only the first row(header) needs 2 line becuase it has an underline
// it returns [ 2, 1, 1, 1, 1, 1, 1, 1 ]
// Why the first element is 2? it's the header. remember UnderlinedCell.prototype.minHeight returns this.inner.minHeight() + 1;
return rows.map(function(row) {
/* remember each row is an array. sth like ->
* [
* TextCell { text: [ 'Kilimanjaro' ] },
* TextCell { text: [ '5895' ] },
* TextCell { text: [ 'Tanzania' ] }
* ]
*/
return row.reduce(function(max, cell) {
// going throw each object in the row array to find minHeight
// objects are sth like --> TextCell { text: [ 'Kilimanjaro' ] }
return Math.max(max, cell.minHeight());
}, 0);
});
}
function colWidths(rows) {
/* This function returns [ 12, 6, 13 ]
* 12 for the first column because "Popocatepetl".length is 12
* 6 for the second column because "height".length is 6
* 13 for thrid column becuase "United States".length is 13
* rows[0] is the header array. We use it to find out how many column we going to have
* i represents number of columns. It will be 0, 1, 2
*/
return rows[0].map(function(_, i) {
return rows.reduce(function(max, row) {
// going through each row which is an array with three objs but only checking its element in index i once for i == 0 (first column/obj), once for i == 1 (second column/obj), once for i == 2 (third column/obj). we wanna find out maximum space that each column needs
return Math.max(max, row[i].minWidth());
}, 0);
});
}
function drawTable(rows) {
// rows is an arry of eight element. one for header and seven for body
/* header ->
* [
* UnderlinedCell { inner: TextCell { text: ['name'] } },
* UnderlinedCell { inner: TextCell { text: ['height'] } },
* UnderlinedCell { inner: TextCell { text: ['country'] } },
* ]
*
* body will be an array of 7 elements each element is an array (containing trhee TextCell objs) that makes one row of final table. sth like ->
* [
* TextCell { text: [ 'Kilimanjaro' ] },
* TextCell { text: [ '5895' ] },
* TextCell { text: [ 'Tanzania' ] }
* ]
*/
var heights = rowHeights(rows);
// heights [ 2, 1, 1, 1, 1, 1, 1, 1 ]
// so we know height of each row
var widths = colWidths(rows);
// widths [ 12, 6, 13 ]
// so we know width of each column
function drawLine(blocks, lineNo) {
// This function returns an string. e.g(I replaced spaces with . to make it more clear) /Kilimanjaro....5895.Tanzania...../
/* what this function does? It takes a blocks(which is an array of arries, composed of three individual block(each block is an array)) and returns an string by joining those blocks with one space
* blocks is sth like:
* [ [ 'Kilimanjaro ' ], [ ' 5895' ], [ 'Tanzania ' ] ]
* or for the header:
* [ [ 'name ', '------------' ],
* [ 'height', '------' ],
* [ 'country ', '-------------' ] ]
* lineNo would be 0 except for header. For header this function will be invoked twice once with lineNo of 0 another with lineNo of 1
* When it comes to header notice that this function will be called once for 0 and once 1. so two seprate line will be generated. once for 0 (that is name, height and country) and once for those underlines (-------)
*/
return blocks.map(function(block) {
return block[lineNo];
})
// so far we have sth like [ 'Kilimanjaro ', ' 5895', 'Tanzania '] and .Join makes it an string
.join(" ");
// Array.prototype.join returns a string
}
function drawRow(row, rowNum) {
/*
* This function returns a string sth like /Kilimanjaro....5895.Tanzania...../ where dots are spaces
* This is a row
* [
* TextCell { text: [ 'Kilimanjaro' ] },
* TextCell { text: [ '5895' ] },
* TextCell { text: [ 'Tanzania' ] }
* ]
* It's cb of rows.map function. So it will be invoked 8 times. once for each row.
* row:
* The current row being processed in the rows. rows[rowNum] is a row
* rowNum:
* The index of the row being processed in the rows.rows[rowNum] is a row. rowNum will be 0 to 7 (all in all 8 row)
*/
var blocks = row.map(function(cell, colNum) {
/*
* this is a cell --> TextCell { text: [ 'Kilimanjaro' ] },
* for each row (remember we are in drawRow. rows.map(drawRow)) blocks would be an array of three element(block). each element, itself, is an array (because TextCell.prototype.draw returns an array)
* for e.g if rowNum == 0 (so it's our header) blocks is.
* [[ 'name ', '------------' ],
* [ 'height', '------' ],
* [ 'country ', '-------------' ] ]
* each inner array has two element because our height for first row is 2. heights[0] == 2
*
* or if rowNum == 1 blocks would
* [ [ 'Kilimanjaro ' ], [ ' 5895' ], [ 'Tanzania ' ] ]
*
* colNum which is index of a given cell in a row indicats where our cell is going to reside (either 0, 1 or 2)
*/
return cell.draw(widths[colNum], heights[rowNum]);
/* remember each cell (TextCell or UnderlinedCell obj) had a draw method on it
* widths [ 12, 6, 13 ]
* heights [ 2, 1, 1, 1, 1, 1, 1, 1 ]
* We send maximum width of a particular column, that is widths[colNum] and
* heights of a particular row, that is heights[rowNum]
*/
});
// by now we have blocks which is sth like --> [ [ 'Kilimanjaro ' ], [ ' 5895' ], [ 'Tanzania ' ] ]
// following return, returns an array of line(s) made by drawLine function. in case the array has two element(line) which is the case for header we joins them with \n.
return blocks[0].map(function(_, lineNo) {
// when rowNum is 0 (so we are working on header) blocks[0] is [ 'name ', '------------' ] so lineNo will be 0 and 1
// other than that lineNo is always 0 because blocks[0] will be sth like [ 'Kilimanjaro ' ]
return drawLine(blocks, lineNo);
// drawLine return an string for each line.
})
// so far we have sth like for the header (an array with two element)
// [ 'name height country ',
// '------------ ------ -------------' ]
// or sth like this for other rows
// [ 'Kilimanjaro 5895 Tanzania ' ]
// we use .join("\n") to make an string out of these arraies
.join("\n");
}
// calling drawRow for each row in rows
// drawRow return an array containing
return rows.map(drawRow)
/* so far we have an array of strings. each elements represent a row. first row has two height but it takes one element "\n" makes it takes two line.
* [ 'name height country \n------------ ------ -------------',
* 'Kilimanjaro 5895 Tanzania ',
* 'Everest 8848 Nepal ',
* 'Mount Fuji 3776 Japan ',
* 'Mont Blanc 4808 Italy/France ',
* 'Vaalserberg 323 Netherlands ',
* 'Denali 6168 United States',
* 'Popocatepetl 5465 Mexico ' ]
* we use .join("\n") to make a giant string out of it
*/
.join("\n");
}
function repeat(string, times) {
var result = "";
for (var i = 0; i < times; i++)
result += string;
return result;
}
function TextCell(text) {
this.text = text.split("\n");
// String.prototype.split return an array of strings
// this.text = ["name"]
// this.text = ["height"]
// this.text = ["country"]
// this.text = ["Kilimanjaro"]
// ...
// TextCell implicitly returns `this`
}
TextCell.prototype.minWidth = function() {
// returns a number indicating this cell’s minimum width (in characters).
return this.text.reduce(function(width, line) {
return Math.max(width, line.length);
}, 0);
};
TextCell.prototype.minHeight = function() {
return this.text.length;
// in our example it's always 1
// number of elements in this.text indicates the minimum height a given cell requires
};
TextCell.prototype.draw = function(width, height) {
// width is maximum width of a particular column that our cell is gonna reside in.
// height is height of a particular row
var result = [];
for (var i = 0; i < height; i++) {
var line = this.text[i] || "";
// e.g line = "Kilimanjaro"
result.push(line + repeat(" ", width - line.length));
// we add extra space in order to align our cells in a particular column.
// we figure out how many expra space we should add with subtracting length of widest cell (width) from length of current cell (line.length)
}
// It returns an array of length *height* e.g result = [ 'Kilimanjaro ' ]
return result;
};
function UnderlinedCell(inner) {
this.inner = inner;
}
UnderlinedCell.prototype.minWidth = function() {
return this.inner.minWidth();
};
UnderlinedCell.prototype.minHeight = function() {
return this.inner.minHeight() + 1;
// so minHeight of the header will be 2
};
UnderlinedCell.prototype.draw = function(width, height) {
// this method returns an array. e.g [ 'name ', '------------' ]
return this.inner.draw(width, height - 1).concat([repeat("-", width)]);
/* why height - 1? because we don't want an empty row to be droned with TextCell.prototype.draw
* instead we draw an underline with the length of our longest cell here.
* keep in mind this function will be used to draw header
*/
};
function dataTable(data) {
var keys = Object.keys(data[0]);
// date[0] -> {name: "Kilimanjaro", height: 5895, country: "Tanzania"},
// keys = [ "name", "height", "country"]
var headers = keys.map(function(name) {
// name for each time -> "name", "height", "country"
return new UnderlinedCell(new TextCell(name));
});
/* headers ->
* [
* UnderlinedCell { inner: TextCell { text: ['name'] } },
* UnderlinedCell { inner: TextCell { text: ['height'] } },
* UnderlinedCell { inner: TextCell { text: ['country'] } },
* ]
*/
var body = data.map(function(row) {
/*
* body will be an array with 7 elements each element is an array (containing trhee objs) that makes one row of final table. sth like ->
* [
* TextCell { text: [ 'Kilimanjaro' ] },
* TextCell { text: [ '5895' ] },
* TextCell { text: [ 'Tanzania' ] }
* ]
*
* each row is sth like -> {name: "Kilimanjaro", height: 5895, country: "Tanzania"},
*/
return keys.map(function(name) {
// name -> name
// name -> height
// name -> country
var value = row[name];
// This was changed:
if (typeof value == "number")
return new RTextCell(String(value));
else
return new TextCell(String(value));
});
});
// console.log([headers].concat(body).length); --> 1 for header and 7 for body -> 8
return [headers].concat(body);
}
function RTextCell(text) {
TextCell.call(this, text);
}
RTextCell.prototype = Object.create(TextCell.prototype);
RTextCell.prototype.draw = function(width, height) {
var result = [];
for (var i = 0; i < height; i++) {
var line = this.text[i] || "";
result.push(repeat(" ", width - line.length) + line);
}
return result;
};
console.log(
drawTable(dataTable(MOUNTAINS))
);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment