24 days of #JavaScriptmas (ongoing)
Tags: css, html, javascript
Published: 2020-12-26 06:24:22

---
title: 24 days of #JavaScriptmas (ongoing)
published: true
date: 2020-12-16 07:02:00 UTC
tags: CSS,HTML,JavaScript
canonical_url: https://drmsite.blogspot.com/2020/12/24-days-of-javascriptmas-ongoing.html
---

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](https://adventofcode.com/), which has been excellent, but my main go-to has been [Scrimba's](https://scrimba.com) [#JavaScriptmas](https://scrimba.com/learn/adventcalendar).

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](https://scrimba.com/learn/adventcalendar/note-at-1-07-cob7f47dea3c2fd0058d165de)

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

### [Day 2](https://scrimba.com/learn/adventcalendar/note-at-0-04-co3bc414e81d84b9a897e955b)

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

### [Day 3](https://scrimba.com/learn/adventcalendar/note-at-0-00-coe1d476790f330187d3139f8)

```javascript
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](https://scrimba.com/learn/adventcalendar/note-at-0-56-cod68426387640050a35e1121)

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

### [Day 5](https://scrimba.com/scrim/co4bd4e3ca57f7203ad255c54)

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

### [Day 6](https://scrimba.com/learn/adventcalendar/note-at-0-16-co1dd454dba9eac6420e2032e)

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

### [Day 7](https://scrimba.com/learn/adventcalendar/note-at-0-53-co48e43e1a99159b8c5688358)

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

### [Day 8](https://scrimba.com/learn/adventcalendar/note-at-1-27-coeaa43338770270431a4af36)

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 <abbr title="To be honest">TBH </abbr>, it was mainly converting it to <abbr title="Sassy CSS -or- Syntactically Awesome Style Sheets">SCSS</abbr>.

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

```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](https://scrimba.com/scrim/co7064e37a7d0bacc354eec55)

```javascript
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](https://scrimba.com/scrim/co2034781aa2e77c0b709867f)

```javascript
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](https://scrimba.com/scrim/co6aa46aca9e7ce6b67f55066)

```javascript
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](https://scrimba.com/scrim/cob4b4d47ae122712a9577832)

```javascript
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](https://scrimba.com/scrim/co6d141f0880ab3fa0516d727)

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

### [Day 14](https://scrimba.com/scrim/co4b14fd58d0c07f2d5ad2715)

```javascript
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](https://scrimba.com/scrim/co8a0497cbd5dc389a0193869)

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
// 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](https://scrimba.com/scrim/co2654bef820b9ad4f1c527f2)

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

### [Day 17](https://scrimba.com/scrim/cof184a0ca3fc88c795a02ae2)

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

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

```javascript
[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:

```javascript
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:

```javascript
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](https://scrimba.com/scrim/co8a34de2aa0b0fbcbbbd22ed)

There was mention of a [for loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for) in the description, but [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) seemed the most suited to this one. I'm really not sure why, but I soirt of get a kick out of using <code>c,i,a</code> as the arguments. <code>c</code> is the current array element, <code>i</code> is the index of that element in the (<code>a</code>) array. Works, doesn't it?

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

Or shrunk:

```javascript
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 <code>byTwo</code> refactor above was a bit pants. We return the appropriately chunked array, but only after removing the extraneous element with the [filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) - this seems inefficient to me. I got to thinking about using a [reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/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](https://stackoverflow.com/questions/32722435/push-wont-work-as-expected-in-reduce#32722612) rather than the array with the new element. That's where [concat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) came to the rescue, now we have:

```javascript
[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 <code>c,i,a</code>, especially as the first value should be another <code>a</code>, to represent the accumulator. Ah well, nothing's perfect I guess, and <code>a,c,i,a</code> won't work. Replacing the first <code>a</code> with <code>n</code> seems to work OK though.

### [Day 19](https://scrimba.com/scrim/co98d49efaae86e1d02317519)

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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt) or a [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set).

```javascript
const alphabetSubsequence = str => 
  str === str.split('')
             .reduce((arr, c) => 
               !!~arr.indexOf(c) 
                 ? arr 
                 : arr.concat([c]), [])
             .sort((a, b) => a.localeCompare(b))
             .join('')
```

```javascript
const alphabetSubsequence = str => 
  str === [...new Set(str.split('')
                         .sort((a, b) => 
                           a > b 
                             ? 1 
                             : a < b 
                               ? -1 
                               : 0))]
                         .join('')
```

```javascript
const alphabetSubsequence = str => 
  str === [...new Set(str.split('')
                         .sort((a, b) => 
                           a.localeCompare(b)
                         )
                     )
          ].join('')
```

```javascript
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:

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

Produces the string <code>"abcd"</code>. Whereas, runing this:

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

Gives us <code>["a", "b", "c", "d"]</code>. This shouldn't be surprising really - I'd just forgotten about how [indexOf](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) and [concat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) can both work on strings as well as arrays. Neat!

### [Day 20](https://scrimba.com/scrim/co77846a18781160a6802524e)

```javascript
// 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 <abbr title="Top-level domains">TLD</abbr>s from [here](https://en.wikipedia.org/wiki/List_of_Internet_top-level_domains). Then I realised I'd be doing that most of the day, so I just added a default <code>unrecognised</code>.

### [Day 21](https://scrimba.com/scrim/co35c45a4b0f6b8b954fbb3e6)

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

```javascript
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:

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

[Perforamce wise, there's not much in it.](https://jsben.ch/tGQDX)

### [Day 22](https://scrimba.com/scrim/cod0a4ba182f658a880587402)

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

That was fun! Using the [concat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) thing I mentioned above within a reduce, such a nice way of generating an array from another.

### [Day 23](https://scrimba.com/scrim/co82647fea42be16db87204d1)

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:

```javascript
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:

```css
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 <code>click</code> 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](https://scrimba.com/scrim/co3b44a33a4f3251b4a39253c)

```javascript
//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.`
}
```