2019-09-26 18:49:05 +00:00
|
|
|
/**
|
|
|
|
* JavaScript Exercise 1
|
|
|
|
* vim: set ts=2 sw=2 et tw=80:
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {number[]} a - The array of numbers.
|
|
|
|
* @param {number} c - The scalar multiplier.
|
|
|
|
* @return {number[]} An array computed by multiplying each element of the
|
|
|
|
* input array `a` with the input scalar value `c`.
|
|
|
|
*/
|
|
|
|
function scalar_product(a, c) {
|
|
|
|
if (!Array.isArray(a)) return undefined;
|
|
|
|
if (!c && c != 0) return a;
|
|
|
|
return a.map(e => e * c);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {number[]} a - The first array of numbers.
|
|
|
|
* @param {number[]} b - The second array of numbers.
|
|
|
|
* @return {number} A value computed by summing the products of each pair
|
|
|
|
* of elements of its input arrays `a`, `b` in the same position.
|
|
|
|
*/
|
|
|
|
function inner_product(a, b) {
|
|
|
|
if (a.length != b.length) return undefined;
|
|
|
|
return a.map((e, i) => e * b[i]).reduce((a, e) => a + e, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {*[]} a - The array.
|
|
|
|
* @param {function} mapfn - The function for the map step.
|
|
|
|
* @param {function} [reducefn= function(x,y) { return x+y; }] - The
|
|
|
|
* function for the reduce step.
|
|
|
|
* @param {string} [seed=""] - The accumulator for the reduce step.
|
|
|
|
* @return {*} The reduced value after the map and reduce steps.
|
|
|
|
*/
|
|
|
|
function mapReduce(a, mapfn, reducefn, seed = "") {
|
|
|
|
if (!Array.isArray(a)) return undefined;
|
|
|
|
if (typeof mapfn != 'function') return undefined;
|
|
|
|
reducefn = reducefn || ((x, y) => x + y);
|
|
|
|
|
|
|
|
for (let i = 0; i < a.length; i++) {
|
|
|
|
seed = reducefn(seed, mapfn(a[i]));
|
|
|
|
}
|
|
|
|
return seed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {number[]} a - The first sorted array of numbers.
|
|
|
|
* @param {number[]} b - The second sorted array of numbers.
|
|
|
|
* @return {number[]} A sorted array with all the elements from
|
|
|
|
* both `a` and `b`.
|
|
|
|
*/
|
|
|
|
function mergeSortedArrays(a, b) {
|
|
|
|
const m = new Array(a.length + b.length);
|
|
|
|
let ac = a.length - 1, bc = b.length - 1, i = ac + bc + 1;
|
|
|
|
|
|
|
|
while (i >= 0) {
|
|
|
|
if (ac < 0 || b[bc] > a[ac]) m[i--] = b[bc--];
|
|
|
|
else m[i--] = a[ac--];
|
|
|
|
}
|
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {integer} x - The first integer.
|
|
|
|
* @param {integer} y - The second integer.
|
|
|
|
* @param {integer} [step=1] - The value to add at each step.
|
|
|
|
* @return {integer[]} An array containing numbers x, x+step, … last, where:
|
|
|
|
* - last equals x + n*step for some n,
|
|
|
|
* - last <= y < last + step if step > 0 and
|
|
|
|
* - last + step < y <= last if step < 0.
|
|
|
|
*/
|
|
|
|
function range(x, y, step = 1) {
|
|
|
|
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(step)
|
|
|
|
|| step == 0) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(x, y, step);
|
|
|
|
if ((x > y && step > 0) || (x < y && step < 0)) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
const a = [];
|
|
|
|
|
|
|
|
while (x < y) {
|
|
|
|
a.push(x);
|
|
|
|
x += step;
|
|
|
|
}
|
|
|
|
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {*[]} a - The array to flatten.
|
|
|
|
* @return {*[]} A flattened array.
|
|
|
|
*/
|
|
|
|
function flatten(arr) {
|
|
|
|
if (!Array.isArray(arr)) {
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const a = [];
|
|
|
|
arr.forEach(e => {
|
|
|
|
e = flatten(e); Array.isArray(e) ? a.push(...e) : a.push(e); });
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {integer} [line_size=72] - The line size.
|
|
|
|
* @return {function} A function that takes a string as an argument
|
|
|
|
* and returns an array of strings where:
|
|
|
|
* a. each string length is no more than `line_size` and
|
|
|
|
* b. doesn't contain line breaks, tabs, double spaces and initial/trailing
|
|
|
|
* white spaces.
|
|
|
|
*/
|
|
|
|
function mkPrettyPrinter(line_size = 72) {
|
|
|
|
if (!Number.isFinite(line_size)) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (s) => {
|
|
|
|
if (typeof s !== 'string') {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
s = s.replace('\n', ' ').trim().replace(/\s+/g, ' ');
|
|
|
|
|
|
|
|
const a = [];
|
|
|
|
let i = -1;
|
|
|
|
const words = s.split(' ');
|
|
|
|
for (const word of words) {
|
|
|
|
if (i == -1 || a[i].length + 1 + word.length > line_size) {
|
|
|
|
a[++i] = word;
|
|
|
|
} else {
|
|
|
|
a[i] += ' ' + word;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {integer} line_size - The line size.
|
|
|
|
* @param {integer} [level=0] level - The indentation level.
|
|
|
|
* @return {function} A function twith the following behavior:
|
|
|
|
* - If called with an integer `n`, change the indentation level by
|
|
|
|
* adding `n`to the current indentation level.
|
|
|
|
* - If called with `true`, return the current indentation level.
|
|
|
|
* - If called with a string:
|
|
|
|
* - break it into lines with length (after adding the indentation)
|
|
|
|
* no more than `line_size`,
|
|
|
|
* - add spaces in front of each line according to the current
|
|
|
|
* indentation level and
|
|
|
|
* - store the resulting lines internally.
|
|
|
|
* - [optional] If called with an array of strings, create an
|
|
|
|
* bullet list (using `*`) taking current indentation level into
|
|
|
|
* account. Also, each element should be properly broken into lines and indented.
|
|
|
|
* - If called with no arguments, produce an array with the lines stored so far.
|
|
|
|
* Internal storage must be emptied. Indentation level must not be changed.
|
|
|
|
*/
|
|
|
|
function mkIndenter(line_size, level=0) {
|
|
|
|
if (!Number.isFinite(line_size) || !Number.isFinite(level)) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
let state = [];
|
|
|
|
return (param) => {
|
|
|
|
if (Number.isInteger(param)) {
|
|
|
|
level += param;
|
|
|
|
} else if (param === true) {
|
|
|
|
return level;
|
|
|
|
} else if (typeof param === 'string') {
|
|
|
|
|
|
|
|
let words = mkPrettyPrinter(line_size - level)(param);
|
2019-09-27 12:35:08 +00:00
|
|
|
words = words.map(e => ' '.repeat(level) + e);
|
2019-09-26 18:49:05 +00:00
|
|
|
state.push(...words);
|
|
|
|
|
|
|
|
} else if (Array.isArray(param)) {
|
|
|
|
for (const e of param) {
|
|
|
|
if (typeof e !== 'string') {
|
|
|
|
return;
|
|
|
|
}
|
2019-09-27 12:35:08 +00:00
|
|
|
const words = mkPrettyPrinter(line_size - level - 2)(e);
|
|
|
|
state.push(' '.repeat(level) + '* ' + words[0]);
|
2019-09-26 18:49:05 +00:00
|
|
|
|
|
|
|
for (let i = 1; i < words.length; i++) {
|
2019-09-27 12:35:08 +00:00
|
|
|
state.push(' '.repeat(level + 2) + words[i]);
|
2019-09-26 18:49:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (param === void(0)) {
|
|
|
|
const a = state;
|
|
|
|
state = [];
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* JavaScript Exercise 2
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates the number of occurrences of letters in a given string
|
|
|
|
* @param {string} a - The input string.
|
|
|
|
* @return {number[]} An array indexed by the letter characters found in the string.
|
|
|
|
*/
|
|
|
|
function letter_frequency(s) {
|
2019-09-27 12:35:08 +00:00
|
|
|
if (typeof s !== 'string') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const a = [];
|
|
|
|
Array.from(s.toLowerCase()).forEach(c => a[c] = !a[c] ? 1 : a[c] + 1);
|
|
|
|
return a;
|
2019-09-26 18:49:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Displays the output of the letter frequency analysis in an HTML table
|
|
|
|
* generated within the `dom` element passed as parameter
|
|
|
|
* @param {number[]} a - The array indexed by the letter characters as returned
|
|
|
|
* from `letter_frequency()`.
|
|
|
|
* @param {Object} dom - The DOM element that will contain the resulting table.
|
|
|
|
* @return {undefined}
|
|
|
|
*/
|
|
|
|
function display_letter_frequency(a, dom) {
|
2019-09-27 12:35:08 +00:00
|
|
|
if (typeof a !== 'object' || typeof dom !== 'object' || !dom) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const t = document.createElement('table');
|
|
|
|
for (const x in a) {
|
|
|
|
const r = document.createElement('tr');
|
|
|
|
const letter = document.createElement('td');
|
|
|
|
const element = document.createElement('td');
|
|
|
|
r.appendChild(letter);
|
|
|
|
r.appendChild(element);
|
|
|
|
letter.innerText = x;
|
|
|
|
element.innerText = '' + a[x];
|
|
|
|
t.appendChild(r);
|
|
|
|
}
|
|
|
|
dom.innerHTML = t.outerHTML;
|
2019-09-26 18:49:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Links the provided input text field in the test page with the table.
|
|
|
|
* @param {Object} inputEl - The DOM object of the input element in test.html.
|
|
|
|
* @return {undefined}
|
|
|
|
*/
|
|
|
|
function online_frequency_analysis(inputEl) {
|
2019-09-27 12:35:08 +00:00
|
|
|
const a = letter_frequency(inputEl.value);
|
|
|
|
display_letter_frequency(a, document.getElementById('frequency_table'));
|
2019-09-26 18:49:05 +00:00
|
|
|
}
|
|
|
|
|
2019-09-27 12:35:08 +00:00
|
|
|
|
2019-09-26 18:49:05 +00:00
|
|
|
/**
|
|
|
|
* JavaScript Exercise 3
|
|
|
|
*/
|
2019-09-27 12:35:08 +00:00
|
|
|
function clock() {
|
|
|
|
const SECOND = 1000;
|
|
|
|
const MINUTE = SECOND * 60;
|
|
|
|
const HOUR = MINUTE * 60;
|
|
|
|
|
|
|
|
const digit = d => (d < 10 ? '0' : '') + d;
|
|
|
|
const format = (h, m, s) => digit(h) + ':' + digit(m) + ':' + digit(s);
|
|
|
|
|
|
|
|
function dateString(date) {
|
|
|
|
const epoch = Number.isInteger(date);
|
|
|
|
const h = epoch ? Math.floor(date / HOUR) : date.getHours();
|
|
|
|
const m = epoch ? Math.floor((date % HOUR) / MINUTE) : date.getMinutes();
|
|
|
|
const s = epoch ? Math.floor((date % MINUTE) / SECOND) : date.getSeconds();
|
|
|
|
return format(h, m, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
const c = document.getElementById('clock');
|
|
|
|
let timer = false;
|
|
|
|
|
|
|
|
setInterval(() => {
|
|
|
|
d = new Date();
|
|
|
|
c.innerHTML = dateString(timer ? d - timer : d);
|
|
|
|
}, SECOND / 10);
|
2019-09-26 18:49:05 +00:00
|
|
|
|
2019-09-27 12:35:08 +00:00
|
|
|
return _ => timer = timer ? false : new Date();
|
2019-09-26 18:49:05 +00:00
|
|
|
}
|