This, that and the other (ramblings of Dominic Myers)
Dominic Myers writes about all sorts of stuff to do with HTML, CSS, JavaScript and a fair chunk of self-indulgent stuff. Thoughts and opinions are all his own, and nothing to do with any employer.
Saturday, 11 February 2023
Friday, 27 January 2023
Smart meter? Snitch meter!
Last night I was woken by a thought - well, that's not correct; I was mainly woken by pins and needles caused by the dog sleeping on my feet! As I wriggled my toes and tried to get the blood flowing again (while ignoring the growls from the dog), I was struck by the thought of energy prices and how they were likely to fall. Wholesale energy prices are falling - some would say plummetting - so I'm guessing that OFGEM will lower the cap sooner rather than later.
Alongside the appreciation that the currently dire situation is likely to change, I remembered something mentioned when I was studying. An old professor asked us to consider how much of our utility bill comprised the cost of the mechanisms employed by calculating our bill. He mentioned that once telephone lines are installed, the outgoings for telephony companies are maintenance and installation of new lines and staff. As such, everything but a small portion of our bills was free and clear profit after paying off the initial investment in infrastructure. He suggested that roughly half of the phone bill was to cover the generation of the bills themselves: the recording of call duration and destination and the postage of those bills to us. Of course, I'm guessing that's changed in these times of online accounts, with that portion of the bill significantly reduced. Have these efficiencies made their way to us as decreasing bills?
I'm reminded of the boon that social media is to the intelligence services - instead of dedicating personnel to track our movements; we do it ourselves.
This reduction was probably the same for the other utilities, such as gas or electricity (let's pop them under the umbrella of energy utilities). I am trying to remember the last time we had any of our meters read - but it was certainly a fair few years ago - thus, the staffing costs have been reduced. This reduction in the number of meter readers has been thanks to smart meters and consumers providing their own readings.
But that's odd, isn't it? Instead of someone coming each quarter to check how much energy has been used in a property and a bill generated from that figure, we now provide that data monthly - or more frequently when we have a smart meter. Now we can tell how much energy we've used and be charged the going rate for that energy at that particular time. December is cold, and the energy price rises; we'll pay more. January is even colder, but energy prices have dropped - do we pay less...? The delay in OFGEM dropping their cap means that we don't - though the utility companies are paying less for the energy they provide.
So what to do? I'm pondering how well I should report my energy consumption. If the cap is high - is it better to report reduced usage as a consumer that provides their readings...? When the cap is lower, I can give increased figures to get me back up to the actual amount. Presumably, that would be far better than relying on the estimated figures used by energy companies - at least for me as a consumer. It does smack of being a gamble, though; who is to say when prices will fall?
Friday, 13 January 2023
TopTracker total hours snippet
I've been using TopTracker for years to keep track of my hours worked. I've recently clocked that I end up firing up a calculator to check how many hours I've worked for a given week; this seemed a little bit silly, TBH, so this morning I created a quick snippet. 5 minutes work now, to save lots of work later seems like a fair trade:
Thursday, 1 December 2022
Scrimba's JavaScriptmas and Advent of Code 2022
Day 1
JavaScriptmas
const panic = str => `${str.toUpperCase().split(' ').join(' 😱 ')}!` console.log(panic("I'm almost out of coffee")) console.log(panic("winter is coming"))
Advent of Code
const elves = input.split('\n\n').map(e => Number(e.split('\n').reduce((a, c) => Number(a) + Number(c)))) console.log(Math.max(...elves)) elves.sort((a, b) => b - a) console.log(elves.slice(0, 3).reduce((a, c) => a + c))
Day 2
JavaScriptmas
const transformData = d => d.map(e => ({ fullName: `${e.name.first} ${e.name.last}`, birthday: new Date(e.dob.date).toDateString() }))
Advent of Code
/** * Rock: A, X. Worth: 1. Beats: Scissors * Paper: B, Y. Worth: 2. Beats: Rock * Scissors: C, Z. Worth: 3. Beats: Paper * 0 if you lost, * 3 if the round was a draw * and 6 if you won */ console.log(input.split('\n').reduce((a, c) => { const [them, me] = c.split(' ') if(them === 'A'){ if(me === 'X'){ a += 3 + 1 } if(me === 'Y'){ a += 6 + 2 } if(me === 'Z'){ a += 0 + 3 } } if(them === 'B'){ if(me === 'X'){ a += 0 + 1 } if(me === 'Y'){ a += 3 + 2 } if(me === 'Z'){ a += 6 + 3 } } if(them === 'C'){ if(me === 'X'){ a += 6 + 1 } if(me === 'Y'){ a += 0 + 2 } if(me === 'Z'){ a += 3 + 3 } } return a }, 0)) /** * Rock: A, X. Worth: 1. Beats: Scissors * Paper: B, Y. Worth: 2. Beats: Rock * Scissors: C, Z. Worth: 3. Beats: Paper * 0 if you lost, * 3 if the round was a draw * and 6 if you won * X: Lose * Y: Draw * Z: Win */ console.log(input.split('\n').reduce((a, c) => { const [them, me] = c.split(' ') if(them === 'A'){ // They've played Rock if(me === 'X'){ // I need to lose a += 0 + 3 // I play Scissors } if(me === 'Y'){ // I need to draw a += 3 + 1 // I play Rock } if(me === 'Z'){ // I need to win a += 6 + 2 // I play Paper } } if(them === 'B'){ // They've played Paper if(me === 'X'){ // I need to lose a += 0 + 1 // I play Rock } if(me === 'Y'){ // I need to draw a += 3 + 2 // I play Paper } if(me === 'Z'){ // I need to win a += 6 + 3 // I play Scissors } } if(them === 'C'){ // They've played Scissors if(me === 'X'){ // I need to lose a += 0 + 2 // I play Paper } if(me === 'Y'){ // I need to draw a += 3 + 3 // I play Scissors } if(me === 'Z'){ // I need to win a += 6 + 1 // I play Rock } } return a }, 0))
Day 3
JavaScriptmas
const faveFoods = { breakfast: 'croissants', lunch: 'pasta', supper: 'pizza' } const {breakfast, lunch, supper} = faveFoods document.getElementById('meals').innerHTML = ` For breakfast, I only like ${breakfast}. For lunch, I love ${lunch}, and for supper I want usually want ${supper}.`
Advent of Code
// drop 96 for lowercase // drop 38 for uppercase const getCodeValue = letter => letter === letter.toLowerCase() ? letter.charCodeAt(0) - 96 : letter.charCodeAt(0) - 38 const getIntersection = (a, b) => { for(let i in a){ if(b.includes(a[i])){ return a[i] } } } const getIntersectionThree = (a, b, c) => { for(let i in a){ if(b.includes(a[i]) && c.includes(a[i])){ return a[i] } } } console.log(input.split('\n').reduce((a, c) => a + getCodeValue(getIntersection(...[c.slice(0, Math.ceil(c.length / 2)).split(''), c.slice(Math.ceil(c.length / 2)).split('')])) , 0)) let total = 0 const lines = input.split('\n') for (let i = 0; i < lines.length; i += 3) { const chunk = lines.slice(i, i + 3); total += getCodeValue(getIntersectionThree(...chunk)) } console.log(total) console.log(input.split('\n').reduce((p, _, i, a) => !(i%3) ? p + getCodeValue(getIntersectionThree(...a.slice(i, i + 3))) : p, 0))
Day 4
JavaScriptmas
const whisper = s => `shh... ${s.endsWith('!') ? s.toLowerCase().slice(0, -1) : s.toLowerCase()}`
Advent of Code
const range = (start, stop) => Array.from({ length: stop - start + 1 }, (_, i) => start + i) const contains = (a, b) => a.every(e => b.includes(e)) || b.every(e => a.includes(e)) const overlaps = (a, b) => a.some(e => b.includes(e)) console.log(input.split('\n').reduce((a, line) => { const [firstArea, secondArea] = line.split(',').map(elf => range(...elf.split('-').map(e => Number(e)))) a.contains += contains(firstArea, secondArea) ? 1 : 0 a.overlaps += overlaps(firstArea, secondArea) ? 1 : 0 return a }, { contains: 0, overlaps: 0 })) // let containedWithin = 0 // let overlaps = 0 // input.split('\n').forEach(line => { // const [firstArea, secondArea] = line.split(',').map(elf => range(...elf.split('-').map(e => Number(e)))) // if(secondArea.every(e => firstArea.includes(e)) || firstArea.every(e => secondArea.includes(e))){ // containedWithin += 1 // } // if(secondArea.some(e => firstArea.includes(e))){ // overlaps += 1 // } // }) // console.log(containedWithin, overlaps)
Day 5
JavaScriptmas
const getSaleItems = d => d.filter(e => e.type === "sweet").map(e => ({item: e.item, price: e.price}))
Advent of Code
const chunk = (str, size) => { const numChunks = Math.ceil(str.length / size) const chunks = new Array(numChunks) for (let i = 0, o = 0; i < numChunks; ++i, o += size) { chunks[i] = str.substr(o, size) } return chunks } const [initialState, instructions] = input.split('\n\n') const initialStateLines = initialState.split('\n').reverse() const positions = chunk(initialStateLines.shift(), 4).reduce((a, c) => { a[Number(c.trim())] = [] return a }, {}) initialStateLines.forEach(line => { chunk(line, 4).forEach((position, index) => { if (position.trim().length) { const { groups: { letter } } = /\s*\[(?<letter>[A-Z])\]\s*/.exec(position) positions[Object.keys(positions)[index]].push(letter) } }) }) /** * Part 1 */ // instructions.split('\n').forEach(instruction => { // const {groups: {num, from, to}} = /move (?<num>[0-9]+) from (?<from>[0-9]+) to (?<to>[0-9]+)/.exec(instruction) // for(let i = 0; i < Number(num); i++){ // positions[Number(to)].push(positions[Number(from)].pop()) // } // }) /** * Part 2 */ instructions.split('\n').forEach(instruction => { const { groups: { num, from, to } } = /move (?<num>[0-9]+) from (?<from>[0-9]+) to (?<to>[0-9]+)/.exec(instruction) positions[Number(to)].push(...positions[Number(from)].slice(-Math.abs(Number(num)))) positions[Number(from)].splice(positions[Number(from)].length - Number(num), Number(num) ) }) console.log(Object.keys(positions).reduce((a, c, i) => { return a + positions[c].at(-1) }, ''))
Day 6
JavaScriptmas
const getRandomNumberOfTacos = () => new Array(Math.floor(Math.random() * (10 - 1) + 1)).fill('🌮')
Advent of Code
const test_input_1 = `mjqjpqmgbljsphdztnvjfqwrcgsmlb` const test_input_2 = `bvwbjplbgvbhsrlpgdmjqwftvncz` const test_input_3 = `nppdvjthqldpwncqszvftbrmjlhg` const test_input_4 = `nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg` const test_input_5 = `zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw` const findPacket = stream => { for(let i = 3; i <= stream.length; i++){ if(new Set(Array.from({length: 4}, (_, a) => stream.charAt(i - a))).size === 4){ return i + 1 } } } const findMessage = stream => { for(let i = 13; i <= stream.length; i++){ if(new Set(Array.from({length: 14}, (_, a) => stream.charAt(i - a))).size === 14){ return i + 1 } } } console.log(findPacket(test_input_1)) console.log(findPacket(test_input_2)) console.log(findPacket(test_input_3)) console.log(findPacket(test_input_4)) console.log(findPacket(test_input_5)) console.log(findPacket(input)) console.log(findMessage(test_input_1)) console.log(findMessage(test_input_2)) console.log(findMessage(test_input_3)) console.log(findMessage(test_input_4)) console.log(findMessage(test_input_5)) console.log(findMessage(input))
Day 7
JavaScriptmas
const altCaps = str => str.split('').map((c, i) => i % 2 ? c : str.charCodeAt(i) > 96 || str.charCodeAt(i) < 123 ? c.toUpperCase() : c).join('')
Advent of Code
// import { Tree } from './Tree.js' // import { TreeNode } from './TreeNode.js' class TreeNode { constructor(key, value = key, parent = null) { this.key = key; this.value = value; this.parent = parent; this.children = []; } get isLeaf() { return this.children.length === 0; } get hasChildren() { return !this.isLeaf; } } class Tree { constructor(key, value = key) { this.root = new TreeNode(key, value); } *preOrderTraversal(node = this.root) { yield node; if (node.children.length) { for (let child of node.children) { yield* this.preOrderTraversal(child); } } } *postOrderTraversal(node = this.root) { if (node.children.length) { for (let child of node.children) { yield* this.postOrderTraversal(child); } } yield node; } insert(parentNodeKey, key, value = key) { for (let node of this.preOrderTraversal()) { if (node.key === parentNodeKey) { node.children.push(new TreeNode(key, value, node)); return true; } } return false; } remove(key) { for (let node of this.preOrderTraversal()) { const filtered = node.children.filter(c => c.key !== key); if (filtered.length !== node.children.length) { node.children = filtered; return true; } } return false; } find(key) { for (let node of this.preOrderTraversal()) { if (node.key === key) return node; } return undefined; } } /* * https://www.30secondsofcode.org/articles/s/js-data-structures-tree */ addEventListener('DOMContentLoaded', async () => { //const res = await fetch("./test.txt") const res = await fetch("./input.txt") const text = await res.text() const input = text.split('\n') let target = null let tree = null let lineParts input.forEach((line, index) => { if (!index) { tree = new Tree('/', 0) target = tree.root } else { if (line.charAt(0) === '$') { lineParts = line.split(' ') if (lineParts.length === 3) { if (lineParts[2] === '..') { target = target.parent } else if (lineParts[2] === '/') { target = tree.root } else { target = target.children.find(ch => ch.key === lineParts[2]) } } } else { lineParts = line.split(' ') if (lineParts[0] === 'dir') { target.children.push(new TreeNode(lineParts[1], 0, target)) } else { const value = Number(lineParts[0]) target.children.push(new TreeNode(lineParts[1], value, target)) target.value += value let parent = target.parent while (parent) { parent.value += value parent = parent.parent } } } } }) const dirs = [...tree.postOrderTraversal()].filter(x => x.hasChildren) console.log('Part 1:', dirs.reduce((a, c) => { if (c.value <= 100000) { a += c.value } return a }, 0)) const spaceRequired = 30000000 - (70000000 - tree.root.value) console.log('Space required: ', spaceRequired) console.log('Part 2:', dirs.filter(dir => { if (dir.value >= spaceRequired) { console.log(dir.key, dir.value) return true } return false }).sort((a, b) => a.value - b.value)[0].value) })
Day 8
JavaScriptmas
const validTime = str => { const [hours, minutes] = str.split(':').map(n => Number(n)) return hours >= 1 && hours <= 24 && minutes >= 0 && minutes < 60 }
Day 9
JavaScriptmas
const capitalizeWord = w => `${w.charAt(0).toUpperCase()}${w.slice(1, w.length)}` const toTitleCase = s => s.split(' ').map(w => capitalizeWord(w)).join(' ')
Day 10
JavaScriptmas
const sortByLength = s => s.sort((a, b) => a.length - b.length)
Day 11
JavaScriptmas
const reverseString = arr => arr.split('').reverse().join('') const reverseStringsInArray = arr => arr.map(a => reverseString(a))
Day 12
JavaScriptmas
class MenuItem extends HTMLElement{ static get observedAttributes() { return [ 'food' ] } constructor() { super() } render() { this.innerHTML = this.html } get html() { return `<div class="food">${this.food}</div>` } attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { this.render() } } set food(v){ this.setAttribute('food', v) } get food() { return this.getAttribute('food') } } window.customElements.define('menu-item', MenuItem) const menu = document.getElementById('menu') const dinnerFoods = ['🍝','🍔','🌮'] menu.innerHTML = dinnerFoods.map(food => `<menu-item food="${food}"/></menu-item>`).join('')
Day 13
JavaScriptmas
const emojifyWord = w => w.charAt(0) === ':' && w.charAt(w.length - 1) === ':' ? emojis[w.slice(1, -1)] ? emojis[w.slice(1, -1)] : w.slice(1, -1) : w const emojifyPhrase = phrase => phrase.split(' ').map(word => emojifyWord(word)).join(' ')
Day 14
JavaScriptmas
const countVowelConsonant = str => str.split('').reduce((a, c) => a += ['a','e','i','o','u'].includes(c) ? 1 : 2, 0)
Day 15
JavaScriptmas
const isPalindrome = str => str === str.split('').reverse().join('')
Day 16
JavaScriptmas
const insertDashes = arr => arr.split(' ').map(w => w.split('').join('-')).join(' ')
Day 17
JavaScriptmas
const flatten = arr => arr.flat() const flatten = arr => Array.prototype.concat.apply([], arr) const flatten = arr => arr.toString().split(',')
Day 18
JavaScriptmas
const candies = (ch, ca) => Math.floor(ca/ch) * ch
Day 19
JavaScriptmas
const centuryFromYear = num => num % 100 === 0 ? num / 100 : Math.floor(num / 100) + 1
Day 20
JavaScriptmas
const getFreePodcasts = data => podcasts.filter(p => !p.paid).map(({title, rating, paid}) => ({title, rating, paid}))
Day 21
JavaScriptmas
const awardBonuses = num => Array.from({length: num}, (_, i) => i + 1).forEach(i => console.log(`${i} - ${!(i%3) && !(i%5) ? 'JACKPOT! 1 Million and a Yacht!' : !(i%3) ? 'Vacation!' : !(i%5) ? '$100,000 bonus!' : ':('}`)) awardBonuses(100)
Day 22
JavaScriptmas
function getReadyTables() { const readyTables = [] for (let i = 0; i < 2; i++) { readyTables.push(Math.round(Math.random()*20) + 1) } return readyTables } const displayTables = () => getReadyTables().map(el => { const table = document.createElement('div') table.classList.add('table') table.innerText = el return table }) document.getElementById('tables').append(...displayTables())
Day 23
JavaScriptmas
const sortProducts = data => data.sort((a, b) => a.price - b.price)
Day 24
JavaScriptmas
const player = document.getElementById("player") const playSong = id => player.src = `https://www.youtube.com/embed/${id}?autoplay=1`
Tuesday, 15 November 2022
Create dot notation array of strings from an Object
Last night, I created an object from an array of strings with dot notation; I needed to do that because I had previously flattened an object into those strings. I did this long and laboriously, but I asked a colleague to see if he could figure out how to do it using recursion. He did, and I'd like to share it here.
If we have this object:
{ "thingOne": { "thingTwo": { "thingThree": true, "thingFour": true } }, "thingyOne": { "thingyTwo": { "thingyThree": true, "thingyFour": true } } }
Then this code:
(() => { const obj = { "thingOne": { "thingTwo": { "thingThree": true, "thingFour": true } }, "thingyOne": { "thingyTwo": { "thingyThree": true, "thingyFour": true } } } const getObjStringsArr = (o = {}, arr = [], name = '') => { Object.keys(o).forEach(key => { if (o[key] === true) { arr.push(`${name}${key}`) } else { const nested = getObjStringsArr(o[key], arr, `${name}${key}.`) arr.concat(nested) } }); return arr } console.log(getObjStringsArr(obj)) })()
It's brilliant having colleagues; it's even better having colleagues with huge brains! Thanks, Hamish!
It's not necessarily faster than my cludge, but it is far more elegant!
Monday, 14 November 2022
Create an Object from dot notation
This was puzzling me all afternoon, and I came up with a bloody terrible work-around using eval
, this evening I decided to do a little more research and found this answer on StackOverflow.
I've adapted it and it seems to work a treat:
(() => { const obj = {} const expand = (output, mystr, value) => { const items = mystr.split(".") // split on dot notation let ref = output // keep a reference of the new object // loop through all nodes, except the last one for (let i = 0; i < items.length - 1; i++) { if (!ref[items[i]]) { // create a new element inside the reference ref[items[i]] = {} } ref = ref[items[i]] // shift the reference to the newly created object } ref[items[items.length - 1]] = value // apply the final value return output // return the full object } const arr = [ "thingOne.thingTwo.thingThree", "thingOne.thingTwo.thingFour", "thingyOne.thingyTwo.thingyThree", "thingyOne.thingyTwo.thingyFour" ] arr.forEach(a => { expand(obj, a, true) }) console.log(obj) })()
I was nearly there on my tod TBH, but wussed out at the end, thisis lovely though! It produces this lovely object:
{ "thingOne": { "thingTwo": { "thingThree": true, "thingFour": true } }, "thingyOne": { "thingyTwo": { "thingyThree": true, "thingyFour": true } } }
Thursday, 6 October 2022
Democide
As reported in The Guardian in 2019, the Institute of Public Policy Research (IPPR) said more than "130,000 deaths in the UK since 2012 could have been prevented if improvements in public health policy had not stalled as a direct result of austerity cuts". More conservatively, the British Medical Journal (BMJ) said that in 2017, austerity was linked to 120,000 extra deaths in England (just in England, not Scotland, Wales or Northern Ireland). In 2018, the Office for National Statistics (ONS) showed a fall in life expectancy for poorer socioeconomic groups and those living in more impoverished areas.
So, it's not like we've not known the Tories have had an appalling impact on our population, but, on top of these utterly shocking figures, we're now presented with yet more. The Independent reported today (05/10/2022), "The UK government's economic policies are 'likely' to have caused a 'great many more deaths' than the Covid Pandemic, an academic has claimed". The Independent used an article from the Journal of Epidemiology and Community Health (JECH) titled "Bearing the burden of austerity: how do changing mortality rates in the UK compare between men and women?" by Walsh, Dundas, McCartney, Gibson and Seaman. Walsh et al. looked at previous statistics, compared actual figures with predicted figures, and came up with the eye-watering figure of 335,000 deaths between 2012 and 2019. Hang about, though; those figures don't include those deaths from the COVID-19 epidemic.
According to the Government's own website today (05/10/2022), there have been 177,977 "deaths within 28 days of being identified as a COVID-19 case by a positive test, reported up to Friday, 20 May 2022". Is anyone else confused by that date? 20 May 2022? Maybe I'm being paranoid; maybe it's not been updated in a while. Perhaps we don't know how many actual deaths from COVID-19 there have been since the start of the Pandemic.
There can be little doubt that the Tories cost lives during the Pandemic. A joint report by the House of Commons Science and Technology Committee and the Health and Social Care Committee condemned severe errors, including delayed lockdowns and how a test, trace and isolate system was set up. It did praise the vaccination programme, though. Funnily enough, the Government took credit for the vaccination programme while crediting the NHS with their disastrous Test and Trace program.
I'm not sure what proportion of that 177,977 needs to be added to the 335,000 death toll of the Tories; whatever figure we'll end up with, it's fair to say that the Tories are guilty of Democide.
If we look at the percentages, the Tories have done a grand job of killing half a per cent of the population; "let the bodies pile high in their thousands", indeed!
The chart at the top, it must be noted, uses a logarithmic scale. So, while the Tories aren't guilty of Mega-murder, nor Deka-mega-murder, they are guilty of Hecto-kilo-murder - and that's us they've been killing (Thank you, Wikipedia, for the pre-fixes)!