Monday, 12 April 2021

Easy setting

"They feel that you're technically excellent and would be a brilliant addition to the team, but that your skill set isn't quite the right fit." I've heard that, or something similar, an awful lot over that over the past few weeks, and I blame video interviews. Let me explain.

My IT career is relatively short, as this is my second career. I had a previous job where I got to a pretty senior position; then, I changed direction to do something I was fascinated by when I could afford to. An off-the-cuff and spontaneous interview later, I landed my first IT gig; I've not looked back.

About to start my third IT role, I'm more than excited by the prospect, but the journey to this position hasn't been all that easy. Several interview processes commenced, and they all started well. There was the screening phone call which went well; then there's the broader - video-based - interview where they can see me and see how old I am. I enjoy this; it's always nice to put a face to the name of the person you've been chatting with.

Then the feedback comes - and, by far, the majority of it runs along the lines of the quote above. I think that's fair enough, TBH; employing someone is nothing that should be taken lightly - you're likely to be spending significant time working alongside them, and if they're old and set in your ways, that could get old, real quick. But how come all the responses are so similar?

I got to chatting with a friend, and she'd been in interviews with her old gaffer and, should a candidate be over the age of 40, there was an automatic black mark against their name. Her gaffer thought that anyone that old would be far too set in their ways to learn something new, let alone work flexibly. Let me tell you: after my previous career, learning and flexibility is all that I want in a role! I guess that's disingenuous, though; there's the opportunity for learning and flexibility in any position; a job is what you make it after all - but IT-based professions seem to relish change and growth.

So I'm left with the feeling of being every-so-slightly discriminated against, thanks to my age. That's not something a middle-aged, middle-class white man can often say. As John Scalzi points out, we've automatically got an advantage and run the game of life on the lowest difficulty setting. It's not the end of the world either; neither does it bear any resemblance to the discrimination faced by others. It simply means that my CV has to be polished a smidge more and that I have to prep a little more. It also means that the process is a little longer and that I hope that the people interviewing today are a little more enlightened than those who interviewed me yesterday.

But, hey, I like meeting new people and talking with them - so I'm sort of odd in that I enjoy interviews. Some people like exams (mentioning no names, but it's my mate I mentioned above), so I guess I'm not that strange in the greater scheme of things. And I might even be wrong; perhaps I wouldn't be quite the right fit? I came across this post on I Programmer and wondered if that would work as a replacement for that final video interview, but I don't think it would. It would be best if you had that face-to-face contact to ensure you're going to be able to work together. Besides, I've been caught out by big-O notation questions before.

My friend has just read this, and she pointed out that her gaffer also thought that if you were over thirty and not in a "lead" position, it meant you weren't any good.

Wednesday, 24 February 2021

Problems with video conferencing

I've been video conferencing for well over three years now at work and don't find it much of a bother. But these three articles might explain why some people do have an issue with it:

Saturday, 13 February 2021

Vuetify and MDB

Both Vuetify and MDB implement the Material Design Design Language. Whereas Vuetify implements Material Design using the Vue JavaScript framework, MDB implements it atop the Bootstrap CSS framework. We're looking at a few specific terms there aren't we? We have JavaScript and CSS frameworks and Design Language - but what do we mean by those terms.

I covered the primary three JavaScript frameworks in my previous book, and there is no shortage of articles comparing and contrasting Angular, React and Vue (the three most popular JavaScript frameworks at the time of writing). A JavaScript framework provides a developer with a blueprint, and often concrete artefacts, to use when building an application. Rather than coding everything from the ground up - perhaps utilising functions from a JavaScript library - a JavaScript framework offers structure (the degree and rigidity of this structure depend upon how opinionated the framework is) that will then be decorated with the application's business logic.

JavaScript frameworks are a vast area of contention so I should note that I use VueJS the most because it's the one I use professionally. I prefer it because of familiarity and because it has the lowest adoption barrier - seemingly being the closest to VanillaJS. Again, their primary benefit is that they allow developers to hit the ground running in terms of how an application will be structured, especially those frameworks that are more opinionated. We've used the word "opinionated" a few times now - it is a term which is closely related to the friction developers find when developing applications. If the developer follows the framework designers' accepted application design, they feel less friction while developing the application. Should they attempt something outside those guidelines, and the framework be very opinionated, then they can find the going harder; they will feel more friction. The constraints of an opinionated framework can be comforting; depending upon the confidence of the developer. JavaScript frameworks do involve some measure of learning as well as obedience to the design dictates of their designers; the less opinionated the framework, the less deference is required to others' decisions, and the developer needs to have more confidence in their abilities. That is not to suggest that skilled developers don't use frameworks - just that they might have chosen their framework because it conforms with their preferred approach.

Similarly, CSS frameworks make the developer's life convenient, and they do that by removing an awful lot of the fear, uncertainty and dread at the start of a project. Initiating a project can be terrifying and might be analogous to an artist creating new work, being confronted with - and subsequently terrified of - the blank canvas. If you've been provided with a ready-made tranche of CSS, then a significant number of visual design and development decisions have already been made for you. While comforting, it is like being swaddled in a vast blanket and can be somewhat constricting. I can't be alone when I noted that many sites started to look like facsimiles of the Bootstrap site just after the framework's release.

This tendency to homogenisation is, to a degree, ameliorated by Bootstrap theming. MDB takes this theming to the next level by adding custom elements and components not ordinarily available within Bootstrap, excluding multiple external libraries.

Criticism of both CSS and JavaScript frameworks is rife and, to an extent, understandable. The constraints they provide, while offering countless boons to the developer, might explain their proliferation. Developers can be opinionated - how else might you interpret the continuing arguments over the relative benefits of Vim over Emacs. Personally, if I have to log into a Linux server, I generally use Nano, much to the disdain of friends who have spent the time to learn the minutiae of Vim or Vi. The criticism means that developers sometimes seek to break out of those confines while, in turn, constraining others within similar bonds of conformity by developing competing CSS or JavaScript frameworks. One has to appreciate such dedication. Developing a framework is a challenging and often thankless task - especially as, once birthed, they are likely to be exposed to a vast ecosystem of other frameworks, all competing for developer adoption.

Developers will be exposed to multiple CSS and JavaScript frameworks throughout their career - either by choice or imposed by their employer. As such, a new developer is left in a quandary about what to learn. The web fundamentals (HTML, CSS and JS) can sometimes be left behind in the scramble to learn the latest framework with the most-posted jobs - concentrating on learning the fundamentals provides a foundation where the developer can learn to appreciate the relative merits of a CSS or JavaScript framework. I once was asked to implement a CSS framework, which I will not name, which was very concrete and opinionated in its naming convention for the classes used. Those classes were the be-all and end-all of the framework and wholly dictated the appearance of elements in the UI. The developer could add class attributes to HTML elements to make them act in ways contrary to how they should by default, which left me feeling very uncomfortable.

That's not to say that developers should ignore innovations within the field, but discernment is required before jumping on to the latest bandwagon. Indeed, the new developer's primary focus should be the fundamentals of HTML, CSS and JS - in that order. Your first professional role, or independent study, will likely provide more than enough exposure to CSS and JavaScript frameworks.

Now that we're more aware of the frameworks lets look at Design Languages. Nate Baldwin suggests that if you have spent any time developing anything on the internet; you have either already created a Design Language of your own, or implemented someone else's. That includes creating an eBay listing or implementing a custom frame on your Facebook Profile. Baldwin's article goes into Design Languages' details. He notes that they are made up of many disparate elements in the same way that our written or spoken languages are. He also points out that, despite their ubiquity, the visual interfaces that Design Languages influence are remarkably complex mechanisms to glean and impart information. As such, we need to be conscious of their impact on our users and aware of their importance.

Being made up of many various elements, a Design Language is a tricky beast and worth studying. Even the name itself can be problematic, with designers calling them Design Languages or Design Systems - some front-end frameworks are even worthy of the name Design Language and cod;tas host a curated list of them. I've used three of the twenty-nine Design Languages listed during my career to date (at the time of writing). Still, of those three, I keep returning to Material Design - though I'm becoming more and more enamoured of IBM's Living Language. I should also note that I've also developed within the constraints of private, corporate, Design Languages for clients, some of which went so far as to have distinct and restricted corporate typefaces.

As an aside, I should also note that when I worked primarily on local government contracts, the predominant thematic colour was purple. After all, Purple was historically restricted to royalty and the elite due to the dye's original exorbitant costs. Thankfully I was no longer working in such a milieu when the UK Independence Party co-opted that particular colour. One can only imagine that the only solution to theming such sites today is to use the whole rainbow of colours represented on Wikipedia's list of United Kingdom political party meta attributes.

As a further aside, I spent some time thinking about political parties' colours in the UK. I wrote a blog post with, not my thoughts per se, but my findings, from the Wikipedia article linked above and display the political parties, sorted using their hue, saturation and lightness (HSL) values.

Working within a Design Language's strictures is similar, but not the same as working within a CSS or JavaScript framework's bindings. It is related in that working with a Design Language means that the overarching application has a consistent look (in the same way as working with a CSS framework) and feel (in the same way as working within a JavaScript framework). Further, introducing elements from other Design Languages is likely to present your users' to some discordance and lead to confusion in the same way as CSS and JavaScript frameworks don't often play well together.

That is not to say we should not seek to challenge our users by introducing innovation. But those challenges should be sprinkled sparingly through the application, rather than at every turn of the user. Using a Design Language, we help our users feel confident that their interaction will lead to expected results by allowing them to feel confident in the application's consistency. Further, a documented Design Language will allow other team members - should you enjoy work with others - feel as though they know how to progress with preliminary development before the input of a dedicated front-end developer. John Rhea discusses the introduction of dissonance to an application in his book Beginner Usability: A Novice's Guide to Zombie Proofing Your Website. He notes that users will be familiar with interacting with websites in specific ways, though interacting with previous websites. He also notes that to introduce dissonance, one must already be conscious of the rules implied by a Design Language.

But what is a Design Language? In the case of Material Design, Material is the metaphor which inspires the Design Language. Real-world objects act as the inspiration for user interface elements. Content is organised using cards, lists and sheets, navigations occurs when users interact with navigation draws and tabs; actions are initiated using buttons. Nearly all elements have a subtle rounding because, in nature, right angles are rare. I'm conscious that answering what a Design Language is is difficult to define; it is an aesthetic feeling towards an application and is made up of visual and conceptual standards. UXPin, the above quote's originator, says that a Design Language collects and standardises user interface components and patterns, a style guide, and some semantics documentation. Both UXPin and Gleb Kuznetsov note that a Design Language must relate to the brand's corporate identity. Should you be tasked with developing an application for a brand, you must examine their other assets - physical or internet-based. This examination will furnish you with a feeling about how your application should look, even if it's only related to any logos to be used or colour-schemes to implement.

We started this by examining what we mean by CSS frameworks, JavaScript frameworks and Design Languages; we'll now look at the relationship between MDB and Vuetify and Material Design. Both MDB and Vuetify implement the Material Design Language, using significantly different techniques. Up until the release of version 5, MDB also required the inclusion of the jQuery JavaScript library. The Bootstrap CSS framework itself required jQuery before version 5; now it only needs the Popper JavaScript library to enable proper positioning of tooltips and popover elements. MDB now has its own, dedicated, JavaScript library and no longer requires jQuery.

MDB adds Material Design concepts to the Bootstrap CSS framework along with a significant number of discrete, JavaScript-powered, user interface elements. Vuetify does pretty much the same but adds Material Design principles to the Vue JavaScript framework. Bootstrap and MDB's reliance on JavaScript means that both approaches aren't all that different, especially when considering the initial reliance Bootstrap had on jQuery. The primary differences are how Vuetify forces the developer to write the application. MDB decorates the HTML, whereas Vuetify replaces common HTML elements with its components. If building with HTML is analogous to building with Lego - which it can sometimes seem to be - then creating your first application with Vuetify is similar to building with a completely different construction toy such as Meccano.

Perhaps I might be accused of being an HTML purist, but using a v-container element is little different to using a div and adding a class of container - but it does seem to be a case of replacing HTML elements for the sake of it. Vuetify does have a sensibly naming convention so that you can mostly guess what is required next while building your application. A v-container likely needs to have a v-row within it, and that v-row is crying out for at least one v-col. You know, seeing as both Bootstrap and Vuetify both share a twelve-point grid system, that the v-col will have a cols attribute with a number between 1 and 12 as its value. But why bother with separate elements when adding a hierarchy of div elements with the classes of container, row and col-* will work just as well? It all smacks of overkill and using custom elements for the sake of it.

I guess that there's not a great deal to differentiate the two approaches to implementing Material Design. Vuetify has the closest affinity to those developers already used to working with Vue. In contrast, MDB is likely to feel the most natural for those developers used to traditional application development using so-called monolithic application structures, which don't take advantage of Single Page Application (SPA) architecture. I am conscious that I don't use SPAs professionally. To a greater or lesser degree, I'm not upset about not working with a SPA though, and I feel an affinity with Chris Ferdinandi when he notes that:

"Browsers are an amazing piece of technology. They give you so much for free, just baked right in."

"Single page apps break all that, and force you to recreate it with JavaScript."

Along with Vuetify, another prominent Vue Component Library implements the Material Design Language, Vue Material. Vue Material is closely related to, and partners with Creative Tim who develop Vue Material Kit. This situation mirrors MDB, as they also offer a paid-for product with more components.

Whatever your chosen CSS or JS framework or Design Language, the important thing is not to confuse your users too much while keeping your clients happy.

Saturday, 6 February 2021

Political Spectrum

I'm doing some writing on design languages at the minute and, in an aside, I talked about working for UK local governments in a previous job. At that time, we used to be tasked with making the primary thematic colour purple because no political party used it. This usage was in the days before the rise of UKIP you see. Anyway, a purple cloth used to be reserved solely for royalty because of the die's exorbitant cost. Finding a list of the colours of all UK political parties on Wikipedia got me thinking about the amount of scripting I'm doing in the context of the 1000 miles in 2021 challenge. So I copied the table from Wikipedia and threw it into a Google Sheet. I converted the colour names into their HEX values then converted them to HSL so that I could sort them properly. Sorted first by Lightness, then Saturation and finally by Hue - I finally output an HTML link which I could display. The following is the sheet script I used to generate the links, and the result is above.

const RGBToHSL = (r,g,b) => {
  // Make r, g, and b fractions of 1
  r /= 255;
  g /= 255;
  b /= 255;
  // Find greatest and smallest channel values
  let cmin = Math.min(r,g,b),
      cmax = Math.max(r,g,b),
      delta = cmax - cmin,
      h = 0,
      s = 0,
      l = 0;
  // Calculate hue
  // No difference
  if (delta == 0){
    h = 0;
  }
  // Red is max
  else if (cmax == r) {
    h = ((g - b) / delta) % 6;
  }
  // Green is max
  else if (cmax == g) {
    h = (b - r) / delta + 2;
  }
  // Blue is max
  else {
    h = (r - g) / delta + 4;
  }
  h = Math.round(h * 60);

  // Make negative hues positive behind 360°
  if (h < 0){
    h += 360;
  }
  // Calculate lightness
  l = (cmax + cmin) / 2;
  // Calculate saturation
  s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
  // Multiply l and s by 100
  s = +(s * 100).toFixed(1);
  l = +(l * 100).toFixed(1);
  return ["hsl(" + h + "," + s + "%," + l + "%)", h, s, l];
}

const hexToRGB = hex => hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, (m, r, g, b) => '#' + r + r + g + g + b + b).substring(1).match(/.{2}/g).map(x => parseInt(x, 16))

const myFunction = () => {
  const returnSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1')
  const rowCount = returnSheet.getLastRow()
  for (let i = rowCount; i > 1; i--) {
    const rawValue = returnSheet.getRange('C' + i).getValue()
    const c = hexToRGB(rawValue)
    const hsl = RGBToHSL(...c)
    const url = returnSheet.getRange('B' + i).getRichTextValue().getLinkUrl()
    returnSheet.getRange('D' + i).setValue(rawValue.toUpperCase())
    returnSheet.getRange('E' + i).setValue(c[0])
    returnSheet.getRange('F' + i).setValue(c[1])
    returnSheet.getRange('G' + i).setValue(c[2])
    returnSheet.getRange('H' + i).setValue(hsl[0])
    returnSheet.getRange('I' + i).setValue(hsl[1])
    returnSheet.getRange('J' + i).setValue(hsl[2])
    returnSheet.getRange('K' + i).setValue(hsl[3])
    if(url){
      returnSheet.getRange('L' + i).setValue(`<a href="${url.replace('/meta/shortname', '').replace('Template:', '')}" target="_blank" style="background-color:${rawValue.toUpperCase().trim()};width:1px;" title="${returnSheet.getRange('B' + i).getValue()}"></a>`)
    }
  }
}

Tuesday, 22 December 2020

Chase the Pizza!

This post is, basically, a collection of random things. The first is a chart from a Google Sheet documenting the distance I've walked over 2021 for the Walk 1000 miles in 2021 challenge. What makes that fun is the way the data is collected. Unfortunately, there's no integration with Polar - my fitness tracker of choice - so I have to enter the steps from the previous day each morning. But then something cool happens. I set up URLooker to act as a webhook for IFTTT, which then slings the data from My Virtual Mission into a Google Sheet. Within the Sheet, there's a script which fires every evening and mangles the data into proper values. The following is the relevant script, and if you can see something that could be improved, please tell me:

const addFormula = () => {
  const factor = 0.62137119
  const returnSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1')
  const rowCount = returnSheet.getLastRow()
  for (let i = rowCount; i > 0; i--) {
    if (returnSheet.getRange('C' + i).getValue() === 'Removed'){
      returnSheet.deleteRow(i);
    } else {
      if(!returnSheet.getRange('F' + i).getValue()){
        const rawValue = returnSheet.getRange('D' + i).getValue()
        const rawValueNumber = Number(rawValue.toString().replace(/\D/g, ""))
        const rawValueUnit = rawValue.toString().replace(/[0-9 ]+/g, "")
        returnSheet.getRange('F' + i).setValue(rawValueNumber)
        returnSheet.getRange('G' + i).setValue(rawValueUnit)
        returnSheet.getRange('H' + i).setValue(rawValueUnit === 'km' ? rawValueNumber * factor : rawValueNumber)
      }
    }
  }
}

This is a cool way of generating an avatar. The following is mine.

And this is my first try at making a game with MakeCode, which is jolly good fun!

Proper link here.

Sunday, 20 December 2020

Type checking JavaScript

I read an article yesterday by Gil Tayar, which was fascinating. In it, he talked about his journey from strongly typed languages to untyped languages and back again. I'd recommend reading the article; it's well written and very informative.

His main gist seemed to be that TypeScript wasn't all that bad. I agree, and I've used it on and off for ages, I think I even have a certificate for a course somewhere, but I'm all for the flexibility of JavaScript.

Anyway, after reading it, I thought I'd give it a go, mostly as I wrote something a little while back which already implemented JSDocs. The process wasn't all that hard but was a bit of a faff. The odd wobble came from destructuring arguments in one of the constructors of one of the toy car child classes, and I was blown if I could understand the differences between String and string types until I read up some more. There were a few such quibbles, and TypeScript had issues with a few of my more relaxed approaches to JavaScript - such as explicitly checking for the existence of variables using !!. You can check out some of the other changes on a branch on GitHub.

I'm not sure I'll do it again, but it was worth spending a little while playing with it last night. It certainly would help in terms of distributing code to other developers, especially in terms of the automatic type checking offered by many IDEs and editors. I'm not overly sure of the strict setting - I like the whole fast and loose aspects of JavaScript quite a lot but, indeed, if I were to upload an NPM package I'd be inclined to be a little more rigorous.

Wednesday, 16 December 2020

2020 24 days of #JavaScriptmas

Xmas can be a somewhat exciting time, but the weather is frightful. We've had snow already this year (IKR! We never get snow before January usually) and the heating has even come on a couple of times during the day! My gaffer at work introduced us to Advent of Code, which has been excellent, but my main go-to has been Scrimba's #JavaScriptmas.

I've been reading about Digital Inclusion in rural communities in my role as a Parish Councillor and so I am as keen as Scrimba to get the word out there about how much fun coding can be.

Now I've been doing this long enough to have picked up all sorts of bad habits and, at work, I have to support IE11, so I rarely get to play with arrow functions or template literals. Except when doing challenges like this, that is: so please do forgive the almost deliberate lack of clarity. I'll document my solutions below, and will continue to add to them as I go along:

Day 1

const candies = (children, candy) => Math.floor(candy / children) * children

Day 2

const depositProfit = (d, r, t) => {
    let y = 0
    while (d <= t) {
        y++
        d += (r / 100) * d
    }
    return y
}

Day 3

const chunkyMonkey = (values, size) => {
  var arr = []
  for (let i = 0, len = values.length; i < len; i += size){
    arr.push(values.slice(i, i + size))
  }
  return arr
}

Day 4

const centuryFromYear = num => Math.floor((!(num % 100) ? num - 1 : num) / 100) + 1

Day 5

//const reverseAString = str => str.split("").reverse().join("")
const reverseAString = (str) => str === "" ? "" : reverseAString(str.substr(1))  + str.charAt(0)

Day 6

const sortByLength = strs => strs.sort((a, b) => a.length - b.length)

Day 7

const countVowelConsonant = str => str.split("").reduce((a, c) => a + (!!~['a','e','i','o','u'].indexOf(c) ? 1 : 2), 0)

Day 8

I'm not going to put the code here, as it's enormous, but most of the heavy lifting is done via CSS with only a tiny bit of JS. I've used this before, and I can't remember where I got the original idea from, but thank you, whoever you are! I've made the odd change to the markup, but not much TBH , it was mainly converting it to SCSS.

Actually, I've changed my mind, here's the CSS:

body {
  background-color: #eee; }

#view[data-value="1"] > .dice {
  transform: rotateY(360deg) !important; }

#view[data-value="2"] > .dice {
  transform: rotateY(-90deg) !important; }

#view[data-value="6"] > .dice {
  transform: rotateY(180deg) !important; }

#view[data-value="4"] > .dice {
  transform: rotateY(90deg) !important; }

#view[data-value="3"] > .dice {
  transform: rotateX(-90deg) !important; }

#view[data-value="5"] > .dice {
  transform: rotateX(90deg) !important; }

#view {
  cursor: pointer;  
  width: 200px;
  height: 200px;
  margin: 100px;
  perspective: 600px; }
  #view .dice {
    width: 200px;
    height: 200px;
    position: absolute;
    transform-style: preserve-3d;
    transition: transform 1s; }
    #view .dice .side {
      position: absolute;
      width: 200px;
      height: 200px;
      background: #fff;
      box-shadow: inset 0 0 40px #ccc;
      border-radius: 40px; }
      #view .dice .side.inner {
        background: #e0e0e0;
        box-shadow: none; }
      #view .dice .side.front {
        transform: translateZ(100px); }
        #view .dice .side.front.inner {
          transform: translateZ(98px); }
      #view .dice .side.right {
        transform: rotateY(90deg) translateZ(100px); }
        #view .dice .side.right.inner {
          transform: rotateY(90deg) translateZ(98px); }
      #view .dice .side.back {
        transform: rotateY(180deg) translateZ(100px); }
        #view .dice .side.back.inner {
          transform: rotateX(-180deg) translateZ(98px); }
      #view .dice .side.left {
        transform: rotateY(-90deg) translateZ(100px); }
        #view .dice .side.left.inner {
          transform: rotateY(-90deg) translateZ(98px); }
      #view .dice .side.top {
        transform: rotateX(90deg) translateZ(100px); }
        #view .dice .side.top.inner {
          transform: rotateX(90deg) translateZ(98px); }
      #view .dice .side.bottom {
        transform: rotateX(-90deg) translateZ(100px); }
        #view .dice .side.bottom.inner {
          transform: rotateX(-90deg) translateZ(98px); }
      #view .dice .side .dot {
        position: absolute;
        width: 46px;
        height: 46px;
        border-radius: 23px;
        background: #444;
        box-shadow: inset 5px 0 10px #000; }
        #view .dice .side .dot.center {
          margin: 77px 0 0 77px; }
        #view .dice .side .dot.dtop {
          margin-top: 20px; }
        #view .dice .side .dot.dleft {
          margin-left: 134px; }
        #view .dice .side .dot.dright {
          margin-left: 20px; }
        #view .dice .side .dot.dbottom {
          margin-top: 134px; }
        #view .dice .side .dot.center.dleft {
          margin: 77px 0 0 20px; }
        #view .dice .side .dot.center.dright {
          margin: 77px 0 0 134px; }

Day 9

const fib = n => {
    const arr = [0, 1]
    let i = 1
    while(arr[arr.length - 1] <= n){
        arr.push(arr[arr.length - 2] + arr[arr.length - 1])
        i++
    }
    arr.pop()
    return arr
}

const sumOddFibonacciNumbers = num => fib(num).reduce((a, c) => Math.abs(c % 2) === 1 ? c + a : a, 0)

Day 10

const byTwo = a => {
    const r = []
    for(let i = 0; i < a.length; i++){
        if(i < a.length - 1 ){
            r.push([a[i], a[i + 1]])
        }
    }
    return r
}

const adjacentElementsProduct = nums => Math.max(...byTwo(nums).map(e => e[0] * e[1]))

Day 11

const avoidObstacles = nums => {
  let n = 2,
    found = false
  while (!found) {
    if (nums.every(num => num % n !== 0)) {
      found = true
    } else {
      n++
    }
  }
  return n
}

Day 12

const validTime = str => parseInt(str.split(":")[0], 10) >= 0 && parseInt(str.split(":")[0], 10) <= 24 && parseInt(str.split(":")[1], 10) >= 0 && parseInt(str.split(":")[1], 10) < 60 

// const validTime = str => ~~str.split(":")[0] >= 0 && ~~str.split(":")[0] <= 24 && ~~str.split(":")[1] >= 0 && ~~str.split(":")[1] < 60 

// In the 24-hour time notation, the day begins at midnight, 00:00, and the last minute of the day begins at 23:59. Where convenient, the notation 24:00 may also be used to refer to midnight at the end of a given date[5] — that is, 24:00 of one day is the same time as 00:00 of the following day.

Day 13

const extractEachKth = (nums, index) => nums.filter((_,i) => (i + 1) % index)

Day 14

const byTwo = a => {
    const r = []
    for(let i = 0; i < a.length; i++){
        if(i < a.length - 1 ){
            r.push([a[i], a[i + 1]])
        }
    }
    return r
}


const arrayMaximalAdjacentDifference = nums => Math.max(...byTwo(nums).map(a => Math.abs(a[0] - a[1])))

Day 15

This one took far too long, mainly because I got distracted by matrix transforms in CSS - horrible! Still, got in there just in time.

// javascript


const previous = document.querySelector(".previous")
const next = document.querySelector(".next")
const gallery = document.querySelector(".gallery")
const cards = Array.from(document.querySelectorAll(".card"))
const matrixR = /matrix\(\s*\d+,\s*\d+,\s*\d+,\s*(\-*\d+),\s*\d+,\s*\d+\)/

gallery.style.transform = window.getComputedStyle(gallery).transform

next.addEventListener('click', e => {
  let current = null
  cards.forEach((c, i) => {
    if(c.classList.contains('current')){
      current = i
    }
  })
  if(current < cards.length - 1){
    cards[current].classList.remove('current')
    cards[current + 1].classList.add('current')
    gallery.style.transform = `matrix(1, 0, 0, 1, ${-220 * (current + 1)}, 0)`
    previous.style.opacity = 1
    if(current + 1 === cards.length - 1){
      next.style.opacity = 0.3
    }
  }
})

previous.addEventListener('click', e => {
  let current = null
  cards.forEach((c, i) => {
    if(c.classList.contains('current')){
      current = i
    }
  })
  if(current > 0){
    cards[current].classList.remove('current')
    cards[current - 1].classList.add('current')
    gallery.style.transform = `matrix(1, 0, 0, 1, ${-220 * (current - 1)}, 0)`
    next.style.opacity = 1
    if(current - 1 === 0){
      previous.style.opacity = 0.3
    }
  }
})

Day 16

const insertDashes = arr => arr.split('').map((c, i, a) => c === ' ' ? ' ' : a[i+1] === ' ' ? c : i < a.length - 1 ? `${c}-` : c ).join('')

Day 17

const differentSymbolsNaive = str => new Set(str.split('')).size

The byTwo function I used in Days 10 and 14 above can be re-written like this:

[1,2,3,4,5,6].map((c, i, a) => a[i+1] && [c, a[i+1]]).filter(e => Array.isArray(e))

Much nicer, and means that Day 10 can be written like this:

const adjacentElementsProduct = nums => Math.max(...nums.map((c, i, a) => a[i+1] && [c, a[i+1]]).filter(e => Array.isArray(e)).map(e => e[0] * e[1]))

With Day 14 written like this:

const arrayMaximalAdjacentDifference = nums => Math.max(...nums.map((c, i, a) => a[i+1] && [c, a[i+1]]).filter(e => Array.isArray(e)).map(a => Math.abs(a[0] - a[1])))

Day 18

There was mention of a for loop in the description, but map seemed the most suited to this one. I'm really not sure why, but I soirt of get a kick out of using c,i,a as the arguments. c is the current array element, i is the index of that element in the (a) array. Works, doesn't it?

const arrayPreviousLess = nums => nums.map((c,i,a) => !i ? -1 : a[i-1] < c ? a[i-1] : -1)

Or shrunk:

const arrayPreviousLess=n=>n.map((c,i,a)=>!i?-1:a[i-1]<c?a[i-1]:-1)

After playing with it some more, I clocked that the byTwo refactor above was a bit pants. We return the appropriately chunked array, but only after removing the extraneous element with the filter - this seems inefficient to me. I got to thinking about using a reduce function instead but got into all sorts of issues trying to return the array after pushing the new element. I had problems because I'd forgotten that push returns the length of the array after the addition rather than the array with the new element. That's where concat came to the rescue, now we have:

[1,2,3,4,5,6].reduce((n,c,i,a)=>(a[i+1])?n.concat([[c,a[i+1]]]):n,[])

Much cleaner, but it messes up my c,i,a, especially as the first value should be another a, to represent the accumulator. Ah well, nothing's perfect I guess, and a,c,i,a won't work. Replacing the first a with n seems to work OK though.

Day 19

That was a fun one, mainly because I wanted to try alternative ways of solving it, and ways of solving it which didn't use charCodeAt or a Set.

const alphabetSubsequence = str => 
  str === str.split('')
             .reduce((arr, c) => 
               !!~arr.indexOf(c) 
                 ? arr 
                 : arr.concat([c]), [])
             .sort((a, b) => a.localeCompare(b))
             .join('')
const alphabetSubsequence = str => 
  str === [...new Set(str.split('')
                         .sort((a, b) => 
                           a > b 
                             ? 1 
                             : a < b 
                               ? -1 
                               : 0))]
                         .join('')
const alphabetSubsequence = str => 
  str === [...new Set(str.split('')
                         .sort((a, b) => 
                           a.localeCompare(b)
                         )
                     )
          ].join('')
const alphabetSubsequence = str => 
  str === [...new Set(str.split('')
                         .sort((a, b) => 
                           a.charCodeAt(0) - b.charCodeAt(0)
                         )
                     )
          ].join('')

I did learn an interesting thing though. Running this:

"abcdcba".split('').reduce((arr, c) => !!~arr.indexOf(c) ? arr : arr.concat([c]))

Produces the string "abcd". Whereas, runing this:

"abcdcba".split('').reduce((arr, c) => !!~arr.indexOf(c) ? arr : arr.concat([c]), [])

Gives us ["a", "b", "c", "d"]. This shouldn't be surprising really - I'd just forgotten about how indexOf and concat can both work on strings as well as arrays. Neat!

Day 20

// Using an Object literal:
function getTLD (tld) {
  const tlds = {
    'org': () => 'organization',
    'com': () => 'commercial',
    'net': () => 'network',
    'info': () => 'information',
    'default': () => 'unrecognised',
  };
  return (tlds[tld] || tlds['default'])();
}

// Using a plain Object:
// const tlds = {
//     org: 'organization',
//     com: 'commercial',
//     net: 'network',
//     info: 'information'
// }
// const domainType = ds => ds.map(d => tlds[d.split('.')[d.split('.').length - 1]])

const domainType = ds => ds.map(d => getTLD(d.split('.')[d.split('.').length - 1]))

I was going to go to town and list all the TLDs from here. Then I realsied I'd be doing that most of the day, so I just added a default unrecognised.

Day 21

Not big nor clever, too tired to be clever today!

const sumOfTwo = (nums1, nums2, value) => {
  for(let i = 0; i < nums1.length; i++){
    for(let j = 0; j < nums2.length; j++){
      if(nums1[i] + nums2[j] === value){
        return true
      }
    }
  }
  return false
}

The coffee kicked in:

const sumOfTwo=(a1,a2,t)=>!!~a1.map(e=>a2.map(a=>a+e)).flat().indexOf(t)

Perforamce wise, there's not much in it.

Day 22

const extractMatrixColumn = (matrix, column) => matrix.reduce((a, c) => a.concat(c[column]), [])

That was fun! Using the concat thing I mentioned above within a reduce, such a nice way of generating an array from another.

Day 23

Insomnia meant I saw the original solution via the email... I was going to do something similar so had to re-think things and this is what I came up with:

document.getElementById('btn').setAttribute('disabled', true)
document.getElementById('string').addEventListener('input', ev => {
  const remaining = 140 - ev.target.value.length
  document.getElementById('counterFooter').textContent = remaining + '/140'
  document.getElementById('counterFooter').style.color = ''
  document.getElementById('btn').removeAttribute('disabled')
  remaining < 0 && document.getElementById('btn').setAttribute('disabled', true)
  remaining === 140 && document.getElementById('btn').setAttribute('disabled', true)   
  remaining < 20 && (document.getElementById('counterFooter').style.color = 'red')
})
document.getElementById('btn').addEventListener('click', ev => {
  window.open(`https://twitter.com/intent/tweet?text=${encodeURI(document.getElementById('string').value)}&hashtags=JavaScriptmas&via=DThompsonDev`)
})

I also tweaked the CSS a smidge:

button{
  width:50%;
  background-color: rgb(29, 161, 242);
  border-radius: 10px;
  padding: 0 10%;
}
button h2{
    color: #fff;
}
button:disabled {
   opacity: .5;
   cursor: default;
}

Not a huge change I know, but I do prefer disabling things via an attribute rather than a class - it also means that the click event handler isn't fired on a button with the disabled attribute set; so less code! Oh, I almost forgot, it tweets too, and with the appropriate hashtag and target too! I've gone back to this and changed the keyup to input, helps a smidge, especially for odd times when input is added via the right-click mechanism.

One more to go, yippee!

Day 24

//EDIT THIS FUNCTION
const spin = async () => {
    //WRITE YOUR CODE HERE
    let counter = 0
    spinningElem.innerHTML = counter
    while(!pushed) {
        await sleep(75) //Paste this wherever you need to sleep the incrimentor 
        counter++
        spinningElem.innerHTML = counter
    }
    stop(counter); //Trigger this function when the STOP button has been pushed 
  
}

//EDIT THIS FUNCTION
function stop(i){
    //WRITE YOUR CODE HERE
    pushed = true
    var result = document.getElementById('result'); //display your result message here
    result.innerHTML = (i === targetInt) ? "Congratulations!" : `Too bad, you were out by ${Math.abs(targetInt - i)}, refresh the page to try again.`
}

Conclusion

Well, that was nice, the certificate for this post came though just now: