Skip to content

Instantly share code, notes, and snippets.

@ENAML
Created August 28, 2018 02:59
Show Gist options
  • Save ENAML/f7b75bb843d74452a6e1fd7cdff95c83 to your computer and use it in GitHub Desktop.
Save ENAML/f7b75bb843d74452a6e1fd7cdff95c83 to your computer and use it in GitHub Desktop.
justify-text-js
const { log } = console;
const SPACE = " ";
const isString = (val) => typeof val === 'string' || val instanceof String;
/**
* Justify Text
* ============
* TODO: description
*/
const justify = (words, lineWidth) => {
// make sure words is an array
words = (isString(words) ? words.split(' ') : words);
words = words
.map((w) => w.trim())
.filter((w) => w.length > 0);
const res = [];
const n = words.length;
let line_st_idx = 0;
let cur_len = words[0].length;
for (let i = 1; i < n; i++) {
const w = words[i];
const w_len = w.length;
const new_len = cur_len + 1 + w_len;
if (new_len > lineWidth) {
res.push(buildLine(words, line_st_idx, i-1, lineWidth));
line_st_idx = i;
cur_len = w_len;
}
else {
cur_len = new_len;
}
}
// add last line
res.push(buildLastLine(words, line_st_idx, n-1, lineWidth));
return res;
// return res.join('\n');
};
/**
* StringBuilder
*/
class StringBuilder {
constructor() {
this._datas = [];
this._length = 0;
}
append(data) {
const dStr = data.toString();
this._length += dStr.length;
this._datas.push(dStr);
}
push(data) { this.append(data) }
get length() { throw new Error("length property not valid") }
length() { return this._length }
toString() { return this._datas.join('') }
}
/**
* TODO - description
*/
function buildLine(words, lo, hi, lineWidth) {
const w_ct = (hi - lo + 1);
const sp_ct = w_ct - 1;
let w_size = 0;
for (let i = lo; i <= hi; i++) w_size += words[i].length;
const sp_size = lineWidth - w_size;
let sp_extra = (sp_ct > 0 ? sp_size % sp_ct : 0);
const sb = new StringBuilder();
sb.push(words[lo]);
for (let i = lo+1; i <= hi; i++) {
let num_spaces = parseInt(sp_size / sp_ct);
if (sp_extra > 0) {
sp_extra--;
num_spaces++;
}
if (sp_ct > 0 && num_spaces > 0) sb.push(SPACE.repeat(num_spaces));
sb.push(words[i]);
}
while (sb.length() < lineWidth) sb.push(SPACE);
return sb.toString();
}
/**
* TODO - description
*/
function buildLastLine(words, lo, hi, lineWidth) {
const sb = new StringBuilder();
sb.push(words[lo]);
for (let i = lo+1; i <= hi; i++) {
sb.push(SPACE);
sb.push(words[i]);
}
while (sb.length() < lineWidth) sb.push(SPACE);
return sb.toString();
}
module.exports = justify;
const justify = require('./justify');
const test = (words, lineWidth) => {
console.log(`words: `, JSON.stringify(words));
console.log(`lineWidth: `, lineWidth);
console.log(`--> res: `);
let res = justify(words, lineWidth);
// pad result like this:
// ┌─────────┬───┐
// │ (index) │ 0 │
// ├─────────┼───┤
// │ 0 │ 0 │
// │ 1 │ 1 │
// └─────────┴───┘
const spaceRow = " ".repeat(res[0].length);
res = [spaceRow, ...res, spaceRow];
const padL = "│ ";
const padR = " │";
res = res
.map((line, i) => padL + line + padR)
const pad = "─".repeat(res[0].length - 2);
const padTop = "┌" + pad + "┐";
const padBtm = "└" + pad + "┘";
res = [padTop, ...res, padBtm]
.join(`\n`);
console.log(res);
console.log(`\n`);
};
test(
["Text", "justification", "is", "trickier", "than", "it", "seems!"],
14,
)
test(
["Listen", "to", "many,", "speak", "to", "a", "few."],
6
)
test(
["The", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dogs."],
11
)
test(
["The", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dogs."],
16
)
test(
["stay", "c", "ova", "bouw", "q", "tinean", "long", "r", "flops", "bl", "bharti", "jack", "prau", "z", "my", "imbe", "b", "wf", "gervas", "akka", "t", "fg", "bals", "israel", "gula", "rly", "cass", "zs", "bu", "ter", "doat", "form", "indulges", "felt", "ll", "pave", "wc", "num", "coendure", "yip", "vers", "wh", "avo", "rig", "ruts", "memo", "doit", "b", "heh", "ctge", "a", "posed", "md", "tannic", "d", "adust", "hf", "rectors", "typw", "crests", "l", "zak", "butted", "o", "waget", "unbeaued", "rekeys", "l", "otus", "reast", "caup", "ggr", "geo", "ofer", "cribs", "gum", "palt", "whew", "outshown", "grig", "qm", "tall", "vo", "dew", "rebellow", "yod", "dhan", "mi", "gtd", "tnt", "buhr", "shut", "elsa", "c", "kors", "pa", "ulu", "mike", "wi", "alec", "z", "bumpy", "vl", "amygdalase", "long", "reis", "sodas", "elix", "tuner", "lo", "outpulls", "goi", "chank", "sow", "assahy", "gaen", "aa", "leukocytic", "plum", "hgt", "funs", "pain", "immeshes", "bats", "fc", "depe", "qv", "esu", "ligroine", "ama", "vaus", "mb", "vac", "pep", "swum", "dust", "bide", "dink", "deb", "arid", "on", "pfx", "waac", "piddler"],
60
)
test(
["unlumbering", "mp", "wamp", "southeast", "bhar", "prospector"],
11
)
test(
["eben", "nv", "aa", "nishada", "db", "hehe", "petrosilicious"],
16
)
test(
["kapa", "marc", "qr", "rotge", "apt"],
5
)
test(
["ki", "dug", "jar", "laps", "eir", "tinc", "op", "ash", "rwd", "fuffy", "act", "baas", "wd", "hod", "reg", "wack", "oud", "bdle", "aw", "urf", "pur", "shamim", "lbs", "waco", "md", "arf", "fwd", "ureal", "abc", "merk", "ux", "wee", "wan", "ficins", "haf", "bone", "tb", "mele", "ur", "snag", "uts", "aivr"],
26
)
test(
["pooh", "cb", "bz", "ceride", "renn", "yese", "ug", "ality", "bono", "adad", "ctg", "goa", "tck", "afb", "fir", "ir", "schute", "jurels", "bey", "lh", "vita", "amir", "roe", "oath", "ht", "timish", "go", "sida", "josh", "dca", "ean", "scuft", "mats", "ss", "uh", "tapa", "fain", "tiens", "apa", "oto", "is", "encysts", "fw", "yow", "chewer", "girl"],
25
)
test(
["sciaenoid", "ass", "engem", "gange", "abb", "xvi", "indwell", "alipeds", "norma", "rt", "hest", "unsainted", "s", "seg", "dovelet", "hoplology", "aq", "raptest", "jem", "inspirers", "wf", "lie", "parachutism", "jour", "shp", "nov", "oda", "dourest", "mamamu", "mlx", "ho", "gid", "blackit", "ld", "j", "trios", "mysis", "leet", "forbite", "diddled", "ordo", "pipilo", "kb", "lim", "elcaja", "fc", "confreres", "tha", "ls", "niggardly", "ctf", "lect", "gitalin", "jo", "yoi", "twi", "et", "p", "kilty", "plasmoquine", "ot", "andy", "john", "db", "ooid", "pars", "qy", "unglossy", "t", "keelfat", "rm","uvid", "realm", "up", "j", "balor", "middleweights", "sui", "rowan", "gis", "quip", "tsks", "blist", "tapen", "razz", "sqrt", "balaic", "undig", "pepsi", "sb", "et", "mavourneen", "chicles", "ife", "merism", "whit", "om", "fugles", "fc", "match", "tef", "no", "r", "stalky", "alaloi", "mb", "taupou", "isopodous", "sa", "basest", "remeasuring", "js", "wauled", "arcane", "fulk", "luncher", "rep", "donny", "ayme", "yoruba", "gau", "stag", "ohm", "ombudsman", "dept", "coed", "subserviate", "xw", "lor", "cronk", "hoe", "seax", "merry", "xu", "sea", "fs", "nucleons", "sct", "mucid", "meek", "aurify", "ecod", "eyn", "weli", "maniples", "pind", "wy", "bkt", "picot", "zip", "tk", "yaw", "humet", "shades", "toba", "nasa", "sel", "tracy", "lt", "sh", "fed", "tr", "roe", "projet", "tendre", "ol", "on", "ferrocene", "caon", "tu", "chi", "millimetre", "mils", "skair", "gree", "dol", "demies", "punk", "duos", "welk", "adj", "do", "maile", "hypnotise", "wl", "bz", "sizars", "aul", "cv", "gu", "pq", "cyul", "waferwork", "dm", "vol", "eleme", "gley", "hypapante", "storey", "thimbled", "mico", "cloky", "idiotry", "mulm", "nt", "cods", "secondary", "cherries", "pg", "luri", "andia", "fee", "lesiy", "dana","nace", "ern", "iyar", "nana", "emailed", "by", "protoamphibian", "cy", "adam", "empair", "drammed", "sapa", "monad", "hunk", "chang", "rwy", "sctd", "jami", "xd", "emend", "pari", "ceca", "putted", "prude", "lym", "lamed", "daub", "v", "rum", "ylems", "ak", "lavenite", "foh", "yird", "iliotibial", "si", "pht", "resteel", "gs", "mew", "forgift", "inn", "fee", "comoid", "scolb", "esd", "jymold", "vest", "koa", "laager"],
80
)
test(
["mn", "chart", "untell", "musks", "ccws", "ure", "kbar", "o", "eld", "im", "aseismicity", "eye", "gibus", "geog", "bual", "crissa", "avion", "near"],
20
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment