Scripting with Javascript

<h3 class="entry-title">Scripting with Javascript</h3>
Posted on



Below is a series of snippets that I have created or adapted from the internet to help me in various projects.


Print Human Readable List

function toHumanReadableList(arr) {
  return [arr.slice(0, -1).join(', '), arr.slice(-1)[0]].join(arr.length < 2 ? '' : ' and ');
}

const a = toHumanReadableList(['Travis']);
const b = toHumanReadableList(['Travis', 'Taylor']);
const c = toHumanReadableList(['Travis', 'Taylor', 'Ashley']);
console.log(a) // Travis
console.log(b) // Travis and Taylor
console.log(c) // Travis, Taylor and Ashley

Exercise. How could the function be extended to include the oxford comma?

 

Swap the position of coordinates in a multidimensional array.

const coordinates = [
    [
        -78.9821121681326,
        -2.88253441285232
    ],
    [
        -78.9822437692326,
        -2.88258803443165
    ],
    [
        -78.9823305858388,
        -2.88262340830617
    ],
    [
        -78.9823383236982,
        -2.88262656128696
    ]
]

const output = coordinates.map(arr => {
    const [lon, lat] = arr;
    return [lat, lon];
})
console.log(JSON.stringify(output, null, 2))


/** OUTPUT **
[
  [
    -2.88253441285232,
    -78.9821121681326
  ],
  [
    -2.88258803443165,
    -78.9822437692326
  ],
  [
    -2.88262340830617,
    -78.9823305858388
  ],
  [
    -2.88262656128696,
    -78.9823383236982
  ]
]
*/

 

Generate a list of cron strings to repeat on day(s) of month for any year

const moment = require('moment')
const dates = [
    moment(new Date('12/24/17')),
    moment(new Date('12/25/17')),
    moment(new Date('12/25/18')),
    moment(new Date('1/1/18')),
    moment(new Date('2/28/18')) 
]
console.log('//-----------------------------------------------------------')
console.log('INPUT')
console.log(dates)
console.log('//-----------------------------------------------------------')
console.log('//-----------------------------------------------------------')
console.log('//  OUTPUT')
const months = {};
dates.forEach( date => {
    const month = date.month() + 1;
    const key = month
    if (months[key]) {
        months[key].push(date.date())
    } else  {
        months[key] = [];
        months[key].push(date.date())
    }
})
const out = Object.keys(months).map( key => {
    return  '0 0 ' + months[key].join(',') + ' ' + key + ' *' 
})
console.log(out)


//-----------------------------------------------------------
INPUT
[ moment("2017-12-24T00:00:00.000"),
  moment("2017-12-25T00:00:00.000"),
  moment("2018-12-25T00:00:00.000"),
  moment("2018-01-01T00:00:00.000"),
  moment("2018-02-28T00:00:00.000") ]
//-----------------------------------------------------------
//-----------------------------------------------------------
//  OUTPUT
[ '0 0 1 1 *', '0 0 28 2 *', '0 0 24,25,25 12 *' ]

 

Building an object out of formatted text.

The below example contains the formatted text content from a PDF. Once the work was done for the initial page,  the remaining pages were able to be processed in a much quicker fashion.

 

const pdfPageTxt = `Familia: Rhamnaceae Aya rambrán Nombre científico: Rhamnus granulosa (Ruiz& Pav.) Weberb. ex M.C. Johnst Otros nombres comunes: “aliso blanco” Hábito: Árbol pequeño de hasta 12 m de altura y 20 cm de DAP. Fuste recto, ramificación de alterna a subopuesta, copa abierta, corteza externa lisa parda grisácea. Hojas: Simples, alternas, de textura coriácea, lámina de forma elíptica u oblongo elíptica (6 a 12 x 3 a 6 cm), ápice de agudo a acuminado, base obtusa y margen aserrado; haz glabro verde oscuro, envés puberulento verde claro. Flores: Reunidas en cimas axilares de 2 a 3 cm de largo. Flores bisexuales, con un tubo flo- ral en forma de copa, cáliz conformado por 5 sépalos de color verde, corola con 5 pétalos cremosos; androceo con 5 estambres insertos en un disco nectarífero adnado al tubo floral; ovario súpero con 3 lóculos. Fruto: Drupa subglobosa de 0,5–0,8 cm de diámetro, de color rojo marrón cuando está ma- dura. Distribución y ecología: Se distribuye en la región andina de Colombia, Ecuador y Perú. En nuestro país crece entre 500 y 3000 m de altitud, en bosques montanos húmedos. Es una especie de bosque maduro, no presenta capacidad de rebrote y su regeneración natural es limitada. En la región florece en los meses de julio y agosto y fructifica en noviembre y diciembre. Sus frutos son consumidos por aves. En los márgenes de los ríos de Cuenca, es una especie poco frecuente y restringida a remanentes de bosque maduro de la zona alta de los ríos Yanuncay y Tomebamba. Usos tradicionales: Su madera se emplea en encofrados para la construcción y en la elabo- ración de marcos y ventanas (Cuamacás & Tipaz, 1995). En algunas comunidades de Imbabu- ra su fruto se consume y en Loja se utiliza como cerca viva (De la Torre et a l, 2008). Propagación: Semillas, para lo cual se deben recolectar los frutos cuando presenten una coloración rojiza.`


const keys = [
    'Familia:',
    'Nombre científico:' ,
    'Otros nombres comunes:',
    // 'Descripción:'
    'Hábito:',
    'Hojas:',
    'Flores:',
    'Fruto:',
    'Distribución y ecología:',
    'Usos tradicionales:',
    'Propagación:'
]


const out = {};

out.familia = pdfPageTxt.split(keys[0])[1]
    .trim()
    .replace(/ .*/, '');

out.nombre = pdfPageTxt.split(keys[0])[1]
    .split(keys[1])[0]
    .replace(out.familia, '')
    .trim()

out.nombre_cientifica = pdfPageTxt.split(keys[1])[1]
    .split(keys[2])[0]
    .trim();

out.otros_nombres_comunes = pdfPageTxt.split(keys[2])[1]
    .split(keys[3])[0]
    .split(',')
    .map(s => s.replace('“', '').replace('”', '').trim());      
  
    

out.habito = pdfPageTxt.split(keys[3])[1]
    .split(keys[4])[0]
    .trim();    

out.hojas = pdfPageTxt.split(keys[4])[1]
    .split(keys[5])[0]
    .trim();  

out.flores = pdfPageTxt.split(keys[5])[1]
    .split(keys[6])[0]
    .trim();  

out.fruta = pdfPageTxt.split(keys[6])[1]
    .split(keys[7])[0]
    .trim();  


out.distribucion_y_ecologia = pdfPageTxt.split(keys[7])[1]
    .split(keys[8])[0]
    .trim();  

out.usos_tradicionales = pdfPageTxt.split(keys[8])[1]
    .split(keys[9])[0]
    .trim();  

out.propagacion = pdfPageTxt.split(keys[9])[1]
    .split(keys[10])[0]
    .trim();  

console.log(out)
{
  "familia": "Rhamnaceae",
  "nombre": "Aya rambrán",
  "nombre_cientifica": "Rhamnus granulosa (Ruiz& Pav.) Weberb. ex M.C. Johnst",
  "otros_nombres_comunes": [
    "aliso blanco"
  ],
  "habito": "Árbol pequeño de hasta 12 m de altura y 20 cm de DAP. Fuste recto, ramificación de alterna a subopuesta, copa abierta, corteza externa lisa parda grisácea.",
  "hojas": "Simples, alternas, de textura coriácea, lámina de forma elíptica u oblongo elíptica (6 a 12 x 3 a 6 cm), ápice de agudo a acuminado, base obtusa y margen aserrado; haz glabro verde oscuro, envés puberulento verde claro.",
  "flores": "Reunidas en cimas axilares de 2 a 3 cm de largo. Flores bisexuales, con un tubo flo- ral en forma de copa, cáliz conformado por 5 sépalos de color verde, corola con 5 pétalos cremosos; androceo con 5 estambres insertos en un disco nectarífero adnado al tubo floral; ovario súpero con 3 lóculos.",
  "fruta": "Drupa subglobosa de 0,5–0,8 cm de diámetro, de color rojo marrón cuando está ma- dura.",
  "distribucion_y_ecologia": "Se distribuye en la región andina de Colombia, Ecuador y Perú. En nuestro país crece entre 500 y 3000 m de altitud, en bosques montanos húmedos. Es una especie de bosque maduro, no presenta capacidad de rebrote y su regeneración natural es limitada. En la región florece en los meses de julio y agosto y fructifica en noviembre y diciembre. Sus frutos son consumidos por aves. En los márgenes de los ríos de Cuenca, es una especie poco frecuente y restringida a remanentes de bosque maduro de la zona alta de los ríos Yanuncay y Tomebamba.",
  "usos_tradicionales": "Su madera se emplea en encofrados para la construcción y en la elabo- ración de marcos y ventanas (Cuamacás & Tipaz, 1995). En algunas comunidades de Imbabu- ra su fruto se consume y en Loja se utiliza como cerca viva (De la Torre et a l, 2008).",
  "propagacion": "Semillas, para lo cual se deben recolectar los frutos cuando presenten una coloración rojiza."
}

Flatten multidimensional Arrays

const arr1 = [ [1,2], [3, 4] ];
const arr2 = [ [1,2], [3, 4, [5, 6] ] ];
const arr3 = [ [1,2], [3, 4, [5, 6, [7, 8] ] ] ];

const flatten = array => array.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);

console.log(flatten(arr1));
console.log(flatten(arr2));
console.log(flatten(arr3));

/****** OUTPUT ************
 * [ 1, 2, 3, 4 ]
 * [ 1, 2, 3, 4, 5, 6 ]
 * [ 1, 2, 3, 4, 5, 6, 7, 8 ]
 *
 */

Test if a string is numeric

function isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

function t (actual, expected) {
    return actual === expected ? 'Passed Test' : `Failed Test - Expected ${expected} got ${actual}`
}

console.log(t(isNumeric('1,110'), true));
process.exit(0);

Determining  if two date ranges overlap

function max(e1, e2){
  return (e1 > e2) ? e1 : e2;
}
function min(e1, e2){
  return (e1 < e2) ? e1 : e2;
}
DateRangesOverlap = max(start1, start2) <= min(end1, end2)


let start1 = new Date('11-3-1990');
let end1 = new Date('11-4-1990')


let start2 = new Date('11-1-1990')
let end2 = new Date('11-5-1990')


console.log(DateRangesOverlap);


/**
 * Compares to comparable objects to find out whether they overlap.
 * It is assumed that the interval is in the format [from,to) (read: from is inclusive, to is exclusive).
 * A null value is interpreted as infinity
 */
function intervalsOverlap(from1, to1, from2, to2) {
   console.log(from1, to1, from2, to2)
    return (to2 === null || from1 < to2) && (to1 === null || to1 > from2);
}


console.log(intervalsOverlap(Date.parse('5-1-16'), Date.parse('5-4-16'), Date.parse('5-5-16'), Date.parse('5-7-16')))
console.log(intervalsOverlap(Date.parse('5-1-16'), Date.parse('5-4-16'), Date.parse('aaaa'), Date.parse('5-7-16')))

 

Getting a semi-random integer.

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min)) + min;
}

 

Getting unique values for a given key on an object

const objs = [
 {
    foo: 'bar',
    bar: 'foo'
 },
 {
   foo: 'baz'
 },
 {
   foo: 'baz'
 }
]

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        if (acc.indexOf(item) === -1) acc.push(item);
        return acc;
    }, []);
}

let distinct_values  = distinct(objs, (i) => i["foo"]);
console.log(distinct_values) // [ 'bar', 'baz' ]

 

Working with Time Strings in HH:mm format + Simple Testing Function 

function t (actual, expected) {
       let has_error = false;
       if (Array.isArray(actual) || Array.isArray(expected)) {
		actual.forEach( (val, i) => {
		  if (actual[i] !== expected[i]) {
 		  	has_error = true;
                  } 
		});
 	}  else if (actual !== expected)  {
		has_error = true;
	}
    	return (has_error) ? `Failed Test - Expected ${expected} got ${actual}` : 'Passed Test';
}


function toSeconds(timeStr) {
    if( typeof timeStr != 'string') {
      return  ['', ''];
    }

    var parts = timeStr.split(':');

    return parts[0] * 3600 + // an hour has 3600 seconds
    parts[1] * 60  // a minute has 60 seconds
}


function getDifferenceBetweenTimeStrings(a, b) {
  return Math.abs(toSeconds(a) - toSeconds(b));
}


// Return an array of length 2. With the Hours, and Minutes component
function getHoursandMinutes(timeInSeconds) {
  return [
    Math.floor(timeInSeconds / 3600), // an hour has 3600 seconds
    Math.floor((timeInSeconds % 3600) / 60)
  ]
}


// Get the Length of between the two times.
const [durationHours, durationMinutes]  =  getHoursandMinutes(getDifferenceBetweenTimeStrings('9:00', '12:17'));

console.log(t([durationHours, durationMinutes], [3, 25]))
console.log(t([durationHours, durationMinutes], [3, 17]))
console.log(t([durationHours, durationMinutes], ['3', 17]))

/****** OUTPUT **************************
 * Failed Test - Expected 3,25 got 3,17
 * Passed Test
 * Failed Test - Expected 3,17 got 3,17
 ****************************************/

 

Using child process to run mongo terminal commands.

const { spawn } = require('child_process');
const ls = spawn('mongorestore', ['-d', 'mean-dev', '--drop', 'dump/mean-dev']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.log(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
  return code;
});

 

Find Simple Duplicates

// Does Not Support Objects. 
const names = ['Travis', 'Calvin', 'Hobbes', 'Hobbes', 1, 1, ,2, 3, 5];

const count = names =>
  names.reduce((a, b) =>
    Object.assign(a, {[b]: (a[b] || 0) + 1}), {})

const duplicates = dict =>
  Object.keys(dict).filter((a) => dict[a] > 1)

console.log(count(names)) 
console.log(duplicates(count(names)))

/******************* OUTPUT ******************************************
 * { '1': 2, '2': 1, '3': 1, '5': 1, Travis: 1, Calvin: 1, Hobbes: 2 }
 * [ '1', 'Hobbes' ]
 *
 **********************************************************************/