Use .filter() Instead of an Inner Loop in My JS Average Temperature Code

image for 'Use .filter() Instead of an Inner Loop in My JS Average Temperature Code' post

See Demo »

My JavaScript functional programming/average temperature tutorial looped through an array of arrays to calculate data and display content. In order to do all this, I used an inner loop with .map() on those inner arrays.

I wasn’t really worried about using an inner loop, but knew the code would look cleaner without it. After publishing that post, I realized I could use .filter() on the inner arrays instead of .map(), which was neater.

The original complete code looked like this:

The HTML…


<div class="temperature-info__container">
 <div id="temperatureHeader" class="temperature-info__header"></div>
 <div id="temperatureInfo"></div>
</div>

The CSS…


.temperature-info__container {
 width: 650px;
}

.temperature-info__header {
  font-weight: bold;
  font-style: italic;
  text-transform: uppercase;
}

.temperature-info__single-temp-row {
  margin-bottom: 10px;
}

.temperature-info__single-temp {
  width: 100px;
  display: inline-block;
  margin-right: 30px;
}

And, of course, the JavaScript…


const temperatureInfo = [
  ["City", "00-08", "08-16", "16-24", "Average"],
  ["Malmö", 12, 16, 9],
  ["Mariestad", 13, 15, 10],
  ["Stockholm", 13, 15, 13],
  ["Upphärad", 14, 16, 15],
  ["Göteborg", 13, 14, 11]
]

function formatData(outerArray) {

  outerArray.map(innerArray => {

    let numbersOnlyList = []

    innerArray.map(index => {
      if(typeof index === "number") {
        return numbersOnlyList.push(index)
      }
    })

    return numbersOnlyList.length
    ?
    displayTemperatureInfo(numbersOnlyList, innerArray[0])
    :
    displayArrayContent(innerArray, "#temperatureHeader")
  })
}

function reducerHelper(accumulator, currentValue) {
  return accumulator + currentValue
}

function displayTemperatureInfo(temperatureArray, getCity) {

  const arrayLength = temperatureArray.length
  const getTemperatureSum = temperatureArray.reduce(reducerHelper)
  const temperatureAverage = getTemperatureSum/arrayLength

  temperatureArray.push(Math.round(temperatureAverage))

  temperatureArray.unshift(getCity)

  return displayArrayContent(temperatureArray, "#temperatureInfo")

}

function displayArrayContent(arrayContent, target) {

  const getTargetElement = document.querySelector(target)
  const parentElement = document.createElement('div')
  parentElement.setAttribute('class', 'temperature-info__single-temp-row')

  arrayContent.map(index => {
    const childElement = document.createElement('span');
    childElement.setAttribute('class', 'temperature-info__single-temp')
    childElement.innerHTML = index
    parentElement.appendChild(childElement)
  })

  return getTargetElement.appendChild(parentElement)

}

formatData(temperatureInfo)

Feel free to look at the previous post to get a full walk-through of this code but right now, I’m only going to look at one specific part…

The inner loop is towards the top of the formatData() function and starts with innerArray.map(). It’s needed to look at any array that has both a city name and numbers, then place the numbers only in an array called numbersOnlyList.

This is what I wanted to refactor with .filter(). To do that while sticking to the JavaScript functional programming paradigm, I first created a separate function defining what I wanted filtered…numbers in this case:


// Place this code below "const temperatureInfo"
function getNumbers(arrayItem) {
 return typeof arrayItem === "number"
}

getNumbers() will be passed as a parameter to .filter(). This can be applied to each inner array to return a “numbers only” array.

We can get rid of that inner loop now. So this code we’re currently using…


...
let numbersOnlyList = []

innerArray.map(index => {
  if(typeof index === "number") {
    return numbersOnlyList.push(index)
  }
})
...

…is completely replaced it with this one line that performs the exact same operation.


...
let numbersOnlyList = innerArray.filter(getNumbers)
...

The complete, updated complete JavaScript code looks like this.


const temperatureInfo = [
  ["City", "00-08", "08-16", "16-24", "Average"],
  ["Malmö", 12, 16, 9],
  ["Mariestad", 13, 15, 10],
  ["Stockholm", 13, 15, 13],
  ["Upphärad", 14, 16, 15],
  ["Göteborg", 13, 14, 11]
]

function getNumbers(arrayItem) {
  return typeof arrayItem === "number"
}

function formatData(outerArray) {

  outerArray.map(innerArray => {

    let numbersOnlyList = innerArray.filter(getNumbers)

    return numbersOnlyList.length
    ?
    displayTemperatureInfo(numbersOnlyList, innerArray[0])
    :
    displayArrayContent(innerArray, "#temperatureHeader")
  })

}

function reducerHelper(accumulator, currentValue) {
  return accumulator + currentValue
}

function displayTemperatureInfo(temperatureArray, getCity) {

  const arrayLength = temperatureArray.length
  const getTemperatureSum = temperatureArray.reduce(reducerHelper)
  const temperatureAverage = getTemperatureSum/arrayLength

  temperatureArray.push(Math.round(temperatureAverage))

  temperatureArray.unshift(getCity)

  return displayArrayContent(temperatureArray, "#temperatureInfo")

}

function displayArrayContent(arrayContent, target) {

  const getTargetElement = document.querySelector(target)
  const parentElement = document.createElement('div')
  parentElement.setAttribute('class', 'temperature-info__single-temp-row')

  arrayContent.map(index => {
    const childElement = document.createElement('span');
    childElement.setAttribute('class', 'temperature-info__single-temp')
    childElement.innerHTML = index
    parentElement.appendChild(childElement)
  })

  return getTargetElement.appendChild(parentElement)

}

formatData(temperatureInfo)

Using inner loops isn’t the performance headache it was years ago as browser technology has gotten better. But refactoring the it out, like I did here, is pretty cool.

Would you like to Tweet this page?