Published on

Kata - Practice Problems

12661 words64 min read
Authors
  • avatar
    Name
    Curtis Warcup
    Twitter
Table of Contents

What is a Kata?

According to Wikipedia, "Kata (型 or 形 literally: "form"), a Japanese word, are detailed choreographed patterns of movements practiced either solo or in pairs."

Kata

We will be using Katas to practice different programming patterns.

Check Air Quality

For this challenge we will implement a function called checkAir(), which will check a collection of air samples.

The function will take in two arguments.

  • The first argument is an array of strings, where each string represents a small air sample that is either clean or dirty.
  • The second argument is a number representing the highest acceptable amount of dirty samples. For example, a threshold of 0.4 means that there must be less than 40% of total samples classified as dirty for our air to be considered clean.

Our function must return Polluted if there are too many dirty air samples, or Clean if the proportion of dirty samples is below the threshold.

const checkAir = function (samples, threshold) {
  const numOfSamples = samples.length
  const dirtySamples = counter(samples, 'dirty')

  return dirtySamples / numOfSamples > threshold ? 'Polluted' : 'Clean'
}

function counter(samples, result) {
  let count = 0
  for (let char of samples) {
    if (char === result) {
      count++
    }
  }
  return count
}

console.log(
  checkAir(
    // should return 'Polluted'
    ['clean', 'clean', 'dirty', 'clean', 'dirty', 'clean', 'clean', 'dirty', 'clean', 'dirty'],
    0.3
  )
)

console.log(
  checkAir(
    // should return 'Polluted'
    ['dirty', 'dirty', 'dirty', 'dirty', 'clean'],
    0.25
  )
)

console.log(
  checkAir(
    // should return 'Clean'
    ['clean', 'dirty', 'clean', 'dirty', 'clean', 'dirty', 'clean'],
    0.9
  )
)

Conditional Sums

Adding only the numbers in the array which match the given condition.

const conditionalSum = function (values, condition) {
  let result = []
  values.map((item) => {
    if (condition === 'even') {
      if (item % 2 === 0) {
        result.push(item)
      }
    } else if (condition === 'odd') {
      if (item % 2 !== 0) {
        result.push(item)
      }
    }
  })
  return result.reduce((partialSum, item) => partialSum + item, 0)
}

// or

const conditionalSum = function (values, condition) {
  let sum = 0
  for (let i = 0; i < values.length; i++) {
    if (condition === 'even' && values[i] % 2 === 0) {
      sum += values[i]
    } else if (condition === 'odd' && values[i] % 2 !== 0) {
      sum += values[i]
    }
  }
  return sum
}

console.log(conditionalSum([1, 2, 3, 4, 5], 'even')) // 6
console.log(conditionalSum([1, 2, 3, 4, 5], 'odd')) // 9
console.log(conditionalSum([13, 88, 12, 44, 99], 'even')) // 144
console.log(conditionalSum([], 'odd')) // 0

Longest Name

Given a list of names, return which instructor has the longest name.

const instructorWithLongestName = function (instructors) {
  let maxIndex = 0

  for (let i = 0; i < instructors.length; i++) {
    if (instructors[i].name.length > instructors[maxIndex].name.length) {
      maxIndex = i
    }
  }
  return instructors[maxIndex]
}

console.log(
  instructorWithLongestName([
    // {name: "Jeremiah", course: "Web"}
    { name: 'Samuel', course: 'iOS' },
    { name: 'Jeremiah', course: 'Web' },
    { name: 'Ophilia', course: 'Web' },
    { name: 'Donald', course: 'Web' },
  ])
)
console.log(
  instructorWithLongestName([
    // {name: "Domascus", course: "Web"}
    { name: 'Matthew', course: 'Web' },
    { name: 'David', course: 'iOS' },
    { name: 'Domascus', course: 'Web' },
  ])
)

Percent Encoded String

Given a normal string of words, turn it into a percent-encoded string by replacing all whitespace with %20. Take a look at the following URL, specifically the last part:

https://www.google.com/search?q=javascript+string+replace+whitespace

This URL will perform a google search for the term "javascript string replace whitespace". Notice that when the string "javascript string replace whitespace" is part of a URL, the space is replaced with %20

For this exercise, focusing on replacing the spaces with %20.

const urlEncode = function (text) {
  return text.replace(/[\s]/g, '%20')
}

// without replace

const urlEncode = function (text) {
  let result = ''

  for (let char of text.trim().split('')) {
    if (char === ' ') {
      char = '%20'
      result = result + char
    } else {
      result = result + char
    }
  }
  return result
}

console.log(urlEncode('Curtis Warcup')) // "Curtis%20Warcup"
console.log(urlEncode(' Curtis Warcup ')) // "%20Curtis%20Warcup%20"
console.log(urlEncode('dogs are super cool')) // "dogs%20are%20super%20cool"

Sum of Largest Numbers

Given an array of 2 or more numbers. Find the two largest numbers in that array, and sum them together.

const sumLargestNumbers = function (data) {
  let num1 = data
    .sort((a, b) => {
      return a - b
    })
    .pop()

  let num2 = data
    .sort((a, b) => {
      return a - b
    })
    .pop()

  return num1 + num2
}

console.log(sumLargestNumbers([1, 10])) // 11
console.log(sumLargestNumbers([1, 2, 3])) // 5
console.log(sumLargestNumbers([10, 4, 34, 6, 92, 2])) // 126

Count Number of Vowels

Counting the number of vowels that appear in a given string.

const numberOfVowels = function (data) {
  let result = data.match(/[aeiou]/gi)
  return (result = result ? result.length : 0)
}

console.log(numberOfVowels('orange')) // 2
console.log(numberOfVowels('Curtis Warcup')) // 4
console.log(numberOfVowels('aeiou')) // 5
console.log(numberOfVowels('')) // 0

Where can I park?

We need to write a function called whereCanIPark() that returns the coordinates of an available parking spot for the vehicle, or returns false if there is no available spot. Our function receives an array of arrays representing parking spots, and a string with type of the vehicle that is looking for a parking spot.

There are three kinds of possible vehicles: regular cars, small cars, and motorcycles.

  • Regular cars can only park in R spots.
  • Small cars can park in R or S spots.
  • Motorcycles can park in R, S, or M spots.

In the array of parking spots, spots are written in both lower-case and upper-case. An upper-case letter means that the particular spot is AVAILABLE, while lower-case letters mean that the spot is UNAVAILABLE.

Our function must return an array with the coordinates of the spot as an [X, Y] pair. See the example input and output below for an illustration.

console.log(
  whereCanIPark(
    // [4, 0]
    [
      // COLUMNS ARE X
      // 0    1    2    3    4    5
      ['s', 's', 's', 'S', 'R', 'M'], // 0 ROWS ARE Y
      ['s', 'M', 's', 'S', 'r', 'M'], // 1
      ['s', 'M', 's', 'S', 'r', 'm'], // 2
      ['S', 'r', 's', 'm', 'r', 'M'], // 3
      ['S', 'r', 's', 'm', 'r', 'M'], // 4
      ['S', 'r', 'S', 'M', 'M', 'S'], // 5
    ],
    'regular'
  )
)

Possible solution:

const whereCanIPark = function (spots, vehicle) {
  const avail = vehicleSpots(vehicle)

  for (let row = 0; row < spots.length; row++) {
    for (let col = 0; col < spots.length; col++) {
      let char = spots[row][col]
      if (avail.indexOf(char) > -1) {
        return new Array(col, row)
      }
    }
  }
  return false
}

function vehicleSpots(vehicle) {
  let avail

  if (vehicle === 'regular') {
    return (avail = 'R')
  } else if (vehicle === 'small') {
    return (avail = 'RS')
  } else if (vehicle === 'motorcycle') {
    return (avail = 'RSM')
  } else {
    return 'Need to add type of vehicle'
  }
}

console.log(vehicleSpots('regular'))
console.log(vehicleSpots('small'))
console.log(vehicleSpots('motorcycle'))

console.log(
  whereCanIPark(
    // [3, 1]
    [
      ['s', 's', 's', 's', 's', 's'],
      ['s', 'm', 's', 'S', 'r', 's'],
      ['s', 'm', 's', 'S', 'r', 's'],
      ['S', 'r', 's', 'm', 'r', 's'],
      ['S', 'r', 's', 'm', 'R', 's'],
      ['S', 'r', 'S', 'M', 'm', 'S'],
    ],
    'motorcycle'
  )
)
console.log(
  whereCanIPark(
    // [4, 0]
    [
      // COLUMNS ARE X
      // 0    1    2    3    4    5
      ['s', 's', 's', 'S', 'R', 'M'], // 0 ROWS ARE Y
      ['s', 'M', 's', 'S', 'r', 'M'], // 1
      ['s', 'M', 's', 'S', 'r', 'm'], // 2
      ['S', 'r', 's', 'm', 'r', 'M'], // 3
      ['S', 'r', 's', 'm', 'r', 'M'], // 4
      ['S', 'r', 'S', 'M', 'M', 'S'], // 5
    ],
    'regular'
  )
)

console.log(
  whereCanIPark(
    // false
    [
      ['M', 'M', 'M', 'M'],
      ['M', 's', 'M', 'M'],
      ['M', 'M', 'M', 'M'],
      ['M', 'M', 'r', 'M'],
    ],
    'small'
  )
)

Repeating Numbers

The input data for this exercise will be two dimensional array (an array of arrays), where each sub-array will have two numeric values. For example:

;[
  [1, 2],
  [2, 3],
]

The first will be the value to repeat, the second will be the amount of times to repeat that value.

const repeatNumbers = function (data) {
  let values = []

  for (let i = 0; i < data.length; i++) {
    let result = ''
    for (let repeats = 0; repeats < data[i][1]; repeats++) {
      result += data[i][0]
    }
    values.push(result)
  }
  return values.join(', ')
}

console.log(repeatNumbers([[1, 10]])) // 1111111111
console.log(
  repeatNumbers([
    [1, 2],
    [2, 3],
  ])
) // 11, 222
console.log(
  repeatNumbers([
    [10, 4],
    [34, 6],
    [92, 2],
  ])
) // 10101010, 343434343434, 9292

Talking Calender

In this activity, we will be converting date strings like "2017/12/02", into more English friendly date strings like "December 2nd, 2017".

We will be given a date as a string (not a Date object). The date will always be formatted as YYYY/MM/DD. We will have to parse the given string and produce a human readable date.

const talkingCalendar = function (date) {
  const monthNames = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
  ]

  let monthIndex = new Date(date).getMonth()
  let year = new Date(date).getFullYear()
  let month = monthNames[monthIndex]
  let day = parseInt(date.slice(8, 10))

  const nth = function (day) {
    if (day > 3 && day < 21) return `${day}th`
    switch (day % 10) {
      case 1:
        return `${day}st`
      case 2:
        return `${day}nd`
      case 3:
        return `${day}rd`
      default:
        return `${day}th`
    }
  }

  return `${month} ${nth(day)}, ${year}`
}

console.log(talkingCalendar('2017/12/02')) // December 2nd, 2017
console.log(talkingCalendar('2007/11/11')) // November 11th, 2007
console.log(talkingCalendar('1987/08/24')) // August 24th, 1987

Change Calculator

Create a function that can calculate which coins we should use when we need to give change.

We will be given two numbers, the total of a transaction, and the amount of cash given to the cashier. Both of these numbers will be represented as whole numbers in cents. Therefore $10 will be represented as 1000.

Our function calculateChange should return an object which describes the total amount of change for the cashier to give back. Although pennies are not used in circulation, we will still calculate the amount of pennies to give back.

Valid denominations are as follows:

  • Twenty dollars
  • Ten dollars
  • Five dollars
  • Two dollars
  • One dollar
  • Quarter (25¢)
  • Dime (10¢)
  • Nickel (5¢)
  • Penny (1¢)

Create a function named calculateChange that takes in a total amount of a bill and the total cash given to pay that bill. Return a new object that describes the total amount of change for the cashier to give back. Omit any types of change that you shouldn't give back, i.e. if you don't give back a twenty dollar bill, don't include it in the results.

const calculateChange = function (total, cash) {
  const getDollarCents = (total, cash) => {
    const amountOwed = cash - total
    const centString = amountOwed.toString()
    const totalCents = centString.slice(centString.length - 2, centString.length)
    const totalDollars = centString.slice(0, centString.length - 2)
    return [totalDollars, totalCents]
  }

  let dollarsOwed = getDollarCents(total, cash)[0]
  let dollarsBack = 0
  let changeObj = {}
  while (dollarsBack < dollarsOwed) {
    if (dollarsBack + 20 <= dollarsOwed) {
      changeObj['twentyDollar'] = Math.floor((dollarsOwed - dollarsBack) / 20)
      dollarsBack += 20 * changeObj['twentyDollar']
    } else if (dollarsBack + 10 <= dollarsOwed) {
      changeObj['tenDollar'] = Math.floor((dollarsOwed - dollarsBack) / 10)
      dollarsBack += 10 * changeObj['tenDollar']
    } else if (dollarsBack + 5 <= dollarsOwed) {
      changeObj['fiveDollar'] = Math.floor((dollarsOwed - dollarsBack) / 5)
      dollarsBack += 5 * changeObj['fiveDollar']
    } else if (dollarsBack + 2 <= dollarsOwed) {
      changeObj['twoDollar'] = Math.floor((dollarsOwed - dollarsBack) / 2)
      dollarsBack += 2 * changeObj['twoDollar']
    } else if (dollarsBack + 1 <= dollarsOwed) {
      changeObj['oneDollar'] = Math.floor((dollarsOwed - dollarsBack) / 1)
      dollarsBack += 1 * changeObj['oneDollar']
    } else {
      break
    }
  }

  let centsOwed = getDollarCents(total, cash)[1]
  let centsBack = 0
  while (centsBack < centsOwed) {
    if (centsBack + 25 <= centsOwed) {
      changeObj['quarter'] = Math.floor((centsOwed - centsBack) / 25)
      centsBack += 25 * changeObj['quarter']
    } else if (centsBack + 10 <= centsOwed) {
      changeObj['dime'] = Math.floor((centsOwed - centsBack) / 10)
      centsBack += 10 * changeObj['dime']
    } else if (centsBack + 5 <= centsOwed) {
      changeObj['nickle'] = Math.floor((centsOwed - centsBack) / 5)
      centsBack += 5 * changeObj['nickle']
    } else if (centsBack + 1 <= centsOwed) {
      changeObj['penny'] = Math.floor(centsOwed - centsBack)
      centsBack += 1 * changeObj['penny']
    } else {
      break
    }
  }

  return changeObj
}

console.log(calculateChange(1787, 2000)) // { twoDollar: 1, dime: 1, penny: 3 }

console.log(calculateChange(2623, 4000))
// { tenDollar: 1, twoDollar: 1, oneDollar: 1, quarter: 3, penny: 2 }
console.log(calculateChange(501, 1000))
// { twoDollar: 2, quarter: 3, dime: 2, penny: 4 }

Case Maker

We will receive a normal string of words separated with spaces as the input. Our job is to convert these strings into camel cased strings.

const camelCase = function (input) {
  // Your code here
}

console.log(camelCase('this is a string')) // thisIsAString
console.log(camelCase('loopy lighthouse')) // loopyLighthouse
console.log(camelCase('supercalifragalisticexpialidocious')) // supercalifragalisticexpialidocious

Possible Solution:

const camelCase = function (input) {
  const lowers = input.toLowerCase().split(' ') // in case any letters are already uppercase
  const result = [lowers.shift()]
  for (let word of lowers) {
    result.push(word[0].toUpperCase() + word.slice(1))
  }
  return result.join('')
}

console.log(camelCase('this is a string')) // thisIsAString
console.log(camelCase('loopy lighthouse')) // loopyLighthouse
console.log(camelCase('supercalifragalisticexpialidocious')) // supercalifragalisticexpialidocious

Case Maker II

We will still be given an input string to convert. However, this time, we'll also be given a casing style to work with. The following code block will describe all the casing styles to support. We may also receive an array of casing styles, and each of these should be applied.

Example:

const makeCase = function(input, case) {
  // Put your solution here
}

console.log(makeCase("this is a string", "camel")); // thisIsAString
console.log(makeCase("this is a string", "pascal"));  // ThisIsAString
console.log(makeCase("this is a string", "snake")); // this_is_a_string
console.log(makeCase("this is a string", "kebab")); // this-is-a-string
console.log(makeCase("this is a string", "title")); // This Is A String
console.log(makeCase("this is a string", "vowel")); // thIs Is A strIng
console.log(makeCase("this is a string", "consonant")); // THiS iS a STRiNG
console.log(makeCase("this is a string", ["upper", "snake"])); // THIS_IS_A_STRING

Precedence of each of the casing styles are as follows, values higher in the list should be processed first:

  1. camel, pascal, snake, kebab, title
  2. vowel, consonant
  3. upper, lower

Our function should be able to handle all of these cases.

For more information on casing styles, read Wikipedia's Special Case Styles for a list of various casing examples.

const makeCase = function (input, cases) {
  function handleCase(input) {
    if (cases.includes('camel')) {
      const lowers = input.toLowerCase().split(' ') // in case any letters are already uppercase
      const result = [lowers.shift()]
      for (let word of lowers) {
        result.push(word[0].toUpperCase() + word.slice(1))
      }
      return result.join('')
    } else if (cases.includes('pascal')) {
      const lowers = input.toLowerCase().split(' ') // in case any letters are already uppercase
      const result = []
      for (let word of lowers) {
        result.push(word[0].toUpperCase() + word.slice(1))
      }
      return result.join('')
    } else if (cases.includes('snake')) {
      return input.toLowerCase().replace(/\s/g, '_')
    } else if (cases.includes('kebab')) {
      return input.toLowerCase().replace(/\s/g, '-')
    } else if (cases.includes('title')) {
      let result = []
      for (let word of input.split(' ')) {
        result.push(word[0].toUpperCase() + word.slice(1))
      }
      return result.join(' ')
    } else {
      return input
    }
  }

  function handleChars(input) {
    if (cases.includes('upper')) {
      return input.toUpperCase()
    } else if (cases.includes('lower')) {
      return input.toLowerCase()
    } else {
      return input
    }
  }

  function handleType(input) {
    if (cases.includes('vowel')) {
      let newWords = ''

      for (let char of input) {
        if (char.match(/[aeiou]/)) {
          newWords += char.toUpperCase()
        } else {
          newWords += char
        }
      }
      return newWords
    } else if (cases.includes('consonant')) {
      let newWords = ''

      for (let char of input) {
        if (char.match(/[bcdfghjklmnpqrstvwxys]/)) {
          newWords += char.toUpperCase()
        } else {
          newWords += char
        }
      }
      return newWords
    } else {
      return input
    }
  }

  let inputCase = handleCase(input)
  let inputType = handleType(inputCase)
  return handleChars(inputType)
}

console.log(makeCase('this is a string', 'camel')) // thisIsAString
console.log(makeCase('this is a string', 'pascal')) // ThisIsAString
console.log(makeCase('this is a string', 'snake')) // this_is_a_string
console.log(makeCase('this is a string', 'kebab')) // this-is-a-string
console.log(makeCase('this is a string', 'title')) // This Is A String
console.log(makeCase('this is a string', 'vowel')) // thIs Is A strIng
console.log(makeCase('this is a string', 'consonant')) // THiS iS a STRiNG
console.log(makeCase('this is a string', ['upper', 'snake'])) // THIS_IS_A_STRING

Multiplication Table

We will be given a number as our input data. This number is the highest value of our multiplication table.

Our job is to generate a multiplication table for the values from 1 to the provided number.

For example:

console.log(multiplicationTable(1))
// expect to return
// 1

console.log(multiplicationTable(5))
// expect to return
// 1 2 3 4 5
// 2 4 6 8 10
// 3 6 9 12 15
// 4 8 12 16 20
// 5 10 15 20 25

console.log(multiplicationTable(10))
// expect to return
// 1 2 3 4 5 6 7 8 9 10
// 2 4 6 8 10 12 14 16 18 20
// 3 6 9 12 15 18 21 24 27 30
// 4 8 12 16 20 24 28 32 36 40
// 5 10 15 20 25 30 35 40 45 50
// 6 12 18 24 30 36 42 48 54 60
// 7 14 21 28 35 42 49 56 63 70
// 8 16 24 32 40 48 56 64 72 80
// 9 18 27 36 45 54 63 72 81 90
// 10 20 30 40 50 60 70 80 90 100

Possible Solution:

const multiplicationTable = function (maxValue) {
  // make array of maxValue number of empty arrays
  let result = []
  for (let i = 0; i < maxValue; i++) {
    result.push([])
  }

  let counter = 1
  let colStart = 0
  let colEnd = maxValue - 1
  let rowStart = 0
  let rowEnd = maxValue - 1

  // top row
  for (let i = colStart; i < maxValue; i++) {
    result[rowStart][i] = counter
    counter++
  }
  rowStart++
  counter = 2

  // left col
  for (let i = rowStart; i < maxValue; i++) {
    result[i][colStart] = counter
    counter++
  }
  colStart++

  // fill the array positions
  while (colStart <= colEnd && rowStart <= rowEnd) {
    if (result[rowStart].length <= maxValue) {
      for (let i = colStart; i <= colEnd; i++) {
        result[rowStart][i] = result[rowStart][0] * result[0][colStart]
        colStart++
      }
    }
    rowStart++
    colStart = 1
  }

  return result
}

console.log(multiplicationTable(1))
// expect to return
// 1

console.log(multiplicationTable(5))
// expect to return
// 1 2 3 4 5
// 2 4 6 8 10
// 3 6 9 12 15
// 4 8 12 16 20
// 5 10 15 20 25

console.log(multiplicationTable(10))
// expect to return
// 1 2 3 4 5 6 7 8 9 10
// 2 4 6 8 10 12 14 16 18 20
// 3 6 9 12 15 18 21 24 27 30
// 4 8 12 16 20 24 28 32 36 40
// 5 10 15 20 25 30 35 40 45 50
// 6 12 18 24 30 36 42 48 54 60
// 7 14 21 28 35 42 49 56 63 70
// 8 16 24 32 40 48 56 64 72 80
// 9 18 27 36 45 54 63 72 81 90
// 10 20 30 40 50 60 70 80 90 100

Square Code

In Square Code, the spaces are removed from the english text and the characters are written into a square (or rectangle). For example, the sentence "If man was meant to stay on the ground god would have given us roots" is 54 characters long, once the spaces are removed, so it is written into a rectangle with 7 rows and 8 columns.

ifmanwas
meanttos
tayonthe
groundgo
dwouldha
vegivenu
sroots

The square root of 54 (Math.sqrt(54)) is 7.3484692283495345. If we round this number up (Math.ceil(Math.sqrt(54))), we get 8. If we use that for the number of characters on each line (the number of columns), then our text will be close to a square shape.

The message is then coded by reading down the columns going left to right. For example, the message above is coded as:

imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau

And that's the output of the algorithm. We can then reverse the process to decrypt the text and read the original message.

const squareCode = (message) => {
  let result = ''
  message = message.replace(/\s/g, '')
  let messArr = []
  let stringLength = Math.ceil(Math.sqrt(message.length))

  while (message !== '') {
    messArr.push(message.slice(0, stringLength))
    message = message.slice(stringLength, message.length)
  }

  for (let i = 0; i < messArr[0].length; i++) {
    result += messArr[0][i]
    for (let j = 1; j < messArr.length; j++) {
      if (messArr[j][i]) {
        result += messArr[j][i]
      }
    }
    result += ' '
  }
  return result
}

// console.log(squareCode('chill out')); // clu hlt io
// console.log(squareCode("feed the dog")); // fto ehg ee dd
// console.log(squareCode("have a nice day")); // hae and via ecy
// console.log(squareCode("if man was meant to stay on the ground god would have given us roots")); // imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau

Queen Threat Detector

In this exercise we will be writing an algorithm, to detect if two queens on a chess board can attack each other.

A game of chess is played on an 8 column by 8 row board. In the game of chess, a queen can attack pieces which are on the same row, column, or diagonal.

Queen movements

In JavaScript, we can represent a chessboard using an 8 by 8 array (8 arrays within an array). For this exercise, our chess board will have 2 queens, and nothing else. A 1 in the array represents a queen on the corresponding square, and a O in the array represents an unoccupied square.

So the following chess board:

chess board example

Would be represented in JavaScript like this:

;[
  [0, 0, 0, 0, 0, 1, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [1, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
]
  1. Our first challenge will be to write a function that generates a chess board like this.

  2. We will then write a function to detect weather or not the two queens are positioned so that they attack each other.

let whiteQueen = [0, 5]
let blackQueen = [5, 0]
let generatedBoard = generateBoard(whiteQueen, blackQueen)
console.log(generatedBoard)
console.log(queenThreat(generatedBoard))

//returns
;[
  [0, 0, 0, 0, 0, 1, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [1, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
]
true

Possible Solution:

const generateBoard = function (whiteQueen, blackQueen) {
  let result = []

  // empty arrays
  for (let i = 0; i < 8; i++) {
    result.push([])
  }

  // blank board, insert 0's
  for (let i = 0; i < 8; i++) {
    while (result[i].length < 8) {
      result[i].push(0)
    }
  }
  // add queen positions
  result[whiteQueen[0]][whiteQueen[1]] = 1
  result[blackQueen[0]][blackQueen[1]] = 1

  return result
}

const queenThreat = function (board) {
  let queenMoves = []

  // find the queens
  for (let i = 0; i < 8; i++) {
    for (let j = 0; j < 8; j++) {
      if (board[i][j] === 1) queenMoves.push([i, j])
    }
  }

  // detect a threat
  if (queenMoves[0][0] - queenMoves[1][0] || queenMoves[0][1] - queenMoves[1][1]) {
    // diagonal if any equal 0
    return true
  } else if (queenMoves[0][0] === queenMoves[1][0] || queenMoves[0][1] === queenMoves[1][1]) {
    // horizontal and vertical
    return true
  } else {
    return false
  }
}

let whiteQueen = [0, 5]
let blackQueen = [5, 0]
let generatedBoard = generateBoard(whiteQueen, blackQueen)
console.log(generatedBoard) // see below
console.log(queenThreat(generatedBoard)) // false

// [  0  1  2  3  4  5  6  7
// 0 [0, 0, 0, 0, 0, 1, 0, 0],
// 1 [0, 0, 0, 0, 0, 0, 0, 0],
// 2 [0, 0, 0, 0, 0, 0, 0, 0],
// 3 [0, 0, 0, 0, 0, 0, 0, 0],
// 4 [0, 0, 0, 0, 0, 0, 0, 0],
// 5 [1, 0, 0, 0, 0, 0, 0, 0],
// 6 [0, 0, 0, 0, 0, 0, 0, 0],
// 7 [0, 0, 0, 0, 0, 0, 0, 0]
// ]

Bouncy Castle

There's a new attraction at this year's Codeville festival. The organizers have decided to bring in several inflatable attractions, but they have no clue how to much blow them up. Each attraction needs to be pumped to a precise volume to achieve maximum festival fun!

The attractions are each made up of a combination of several different shapes: cones, spheres and prisms. For example, the giant inflatable duck is made up of two spheres (the body and head) and a cone (the beak) 🦆..

Each shape has a different calculation for determining volume, so we'll need to create a few functions that will help us figure out the volume of the various inflatable attractions.

In this challenge, we'll need to implement four functions.

The first three will calculate the volume of the individual shapes:

  • sphereVolume() will calculate the volume of a sphere given a radius
  • coneVolume() will calculate the volume of a cone given a radius and a height
  • prismVolume() will calculate the volume of a prism given a height, a width, and a depth

The fourth function, totalVolume(), will receive an array containing the different shapes that make up a single attraction. The totalVolume function should use the previous three functions to calculate the total volume of an attraction.

const PI = 3.14159

const sphereVolume = function (radius) {
  return (4 / 3) * PI * radius ** 3
}

const coneVolume = function (radius, height) {
  let base = PI * radius * radius
  return (1 / 3) * base * height
}

const prismVolume = function (height, width, depth) {
  return height * width * depth
}

const volumes = function (solid) {
  switch (solid.type) {
    case 'sphere':
      return sphereVolume(solid.radius)
    case 'cone':
      return coneVolume(solid.radius, solid.height)
    case 'prism':
      return prismVolume(solid.height, solid.width, solid.depth)
    default:
      return 'Invalid solid type'
  }
}

const totalVolume = function (solids) {
  return solids.reduce((lastVolume, solid) => {
    return lastVolume + volumes(solid)
  }, 0)
}

const largeSphere = {
  type: 'sphere',
  radius: 40,
}

const smallSphere = {
  type: 'sphere',
  radius: 10,
}

const cone = {
  type: 'cone',
  radius: 3,
  height: 5,
}

const duck = [largeSphere, smallSphere, cone]

console.log(sphereVolume(1))
console.log(4186 < sphereVolume(10) && sphereVolume(10) < 4189) // true

console.log(coneVolume(8, 18))
console.log(45 < coneVolume(3, 5) && coneVolume(3, 5) < 49) // true

console.log(prismVolume(9, 7, 13))
console.log(prismVolume(3, 4, 5) === 60) // true

console.log(272000 < totalVolume(duck) && totalVolume(duck) < 275000) // true

The Great Codeville Bake-off

The town festival is tomorrow and the organizers have only just realized that they've booked two bakeries to cater desserts, but only have one kitchen available. Instead of just choosing one bakery, let's help them figure out a way to work together.

Both of the bakeries have a specialty, so they each have a stock of specific ingredients.

Lucky for the festival organizers, we've found a recipe book in the town library with TONS of fusion recipes, unfortunately it's thousands of pages long and we don't have much time. Let's write a function that helps determine which recipes match the ingredients that both bakeries have in stock.

We need to complete a function called chooseRecipe(), which will receive three parameters:

  • An array of ingredients in stock at Bakery A
  • An array of ingredients in stock at Bakery B
  • An array of recipe objects.

Each recipe has a name property(string) and an ingredient property(array).

We are limiting our search to two ingredient recipes. We want to find a recipe that utilizes one ingredient from Bakery A and one from Bakery B.

Our chooseRecipe() function should return the name of the correct recipe.

There will always be a single correct answer, and you will NOT need to consider special cases. For example, you do NOT need to worry about cases where one bakery has BOTH the ingredients for a recipe.

Example:

const chooseRecipe = function (bakeryA, bakeryB, recipes) {
  // Code here!
}

let bakeryA = ['saffron', 'eggs', 'tomato paste', 'coconut', 'custard']
let bakeryB = ['milk', 'butter', 'cream cheese']
let recipes = [
  {
    name: 'Coconut Sponge Cake',
    ingredients: ['coconut', 'cake base'],
  },
  {
    name: 'Persian Cheesecake',
    ingredients: ['saffron', 'cream cheese'],
  },
  {
    name: 'Custard Surprise',
    ingredients: ['custard', 'ground beef'],
  },
]

console.log(chooseRecipe(bakeryA, bakeryB, recipes)) // outputs 'Persian Cheesecake'

bakeryA = ['potatoes', 'bay leaf', 'raisins']
bakeryB = ['red bean', 'dijon mustard', 'apples']
recipes = [
  {
    name: 'Potato Ganache',
    ingredients: ['potatoes', 'chocolate'],
  },
  {
    name: 'Sweet Fish',
    ingredients: ['anchovies', 'honey'],
  },
  {
    name: "Nima's Famous Dijon Raisins",
    ingredients: ['dijon mustard', 'raisins'],
  },
]

console.log(chooseRecipe(bakeryA, bakeryB, recipes)) // outputs 'Nima's Famous Dijon Raisins'

Possible Solution:

const chooseRecipe = function (bakeryA, bakeryB, recipes) {
  let listA = ingredientCheck(bakeryA, recipes)
  let listB = ingredientCheck(bakeryB, recipes)

  return listA.filter((recipe) => recipe.includes(listB))
}

const ingredientCheck = function (bakery, recipes) {
  let recipeList = []

  for (let i = 0; i < recipes.length; i++) {
    for (let j = 0; j < bakery.length; j++) {
      if (recipes[i].ingredients.includes(bakery[j])) {
        recipeList.push(recipes[i].name)
      }
    }
  }
  return recipeList
}

let bakeryA = ['saffron', 'eggs', 'tomato paste', 'coconut', 'custard']
let bakeryB = ['milk', 'butter', 'cream cheese']
let recipes = [
  {
    name: 'Coconut Sponge Cake',
    ingredients: ['coconut', 'cake base'],
  },
  {
    name: 'Persian Cheesecake',
    ingredients: ['saffron', 'cream cheese'],
  },
  {
    name: 'Custard Surprise',
    ingredients: ['custard', 'ground beef'],
  },
]

console.log(chooseRecipe(bakeryA, bakeryB, recipes)) // outputs 'Persian Cheesecake'

bakeryA = ['potatoes', 'bay leaf', 'raisins']
bakeryB = ['red bean', 'dijon mustard', 'apples']
recipes = [
  {
    name: 'Potato Ganache',
    ingredients: ['potatoes', 'chocolate'],
  },
  {
    name: 'Sweet Fish',
    ingredients: ['anchovies', 'honey'],
  },
  {
    name: "Nima's Famous Dijon Raisins",
    ingredients: ['dijon mustard', 'raisins'],
  },
]

console.log(chooseRecipe(bakeryA, bakeryB, recipes)) // outputs 'Nima's Famous Dijon Raisins'

Organizing Instructors

In this exercise, we will be given a list of instructors and we will create a single object to organize them based on their course.

Example:

const organizeInstructors = function (instructors) {
  // Put your solution here
}

console.log(
  organizeInstructors([
    { name: 'Samuel', course: 'iOS' },
    { name: 'Victoria', course: 'Web' },
    { name: 'Karim', course: 'Web' },
    { name: 'Donald', course: 'Web' },
  ])
)

// outcome
// {
//   iOS: ["Samuel"],
//   Web: ["Victoria", "Karim", "Donald"]
// }

console.log(
  organizeInstructors([
    { name: 'Brendan', course: 'Blockchain' },
    { name: 'David', course: 'Web' },
    { name: 'Martha', course: 'iOS' },
    { name: 'Carlos', course: 'Web' },
  ])
)

// oputcome
// {
//   Blockchain: ["Brendan"],
//   Web: ["David", "Carlos"],
//   iOS: ["Martha"]
// }

Create a function named organizeInstructors() that will receive an array of instructor objects, and will return a new object that has the following format:

{
  CourseName: [instructors]
}

Possible Solution:

const organizeInstructors = function (instructors) {
  let result = {}

  for (let item of instructors) {
    let course = item.course
    if (!result[course]) {
      result[course] = [item.name]
    } else {
      result[course].push(item.name)
    }
  }
  return result
}

console.log(
  organizeInstructors([
    { name: 'Samuel', course: 'iOS' },
    { name: 'Victoria', course: 'Web' },
    { name: 'Karim', course: 'Web' },
    { name: 'Donald', course: 'Web' },
  ])
)

// outcome
// {
//   iOS: ["Samuel"],
//   Web: ["Victoria", "Karim", "Donald"]
// }

console.log(
  organizeInstructors([
    { name: 'Brendan', course: 'Blockchain' },
    { name: 'David', course: 'Web' },
    { name: 'Martha', course: 'iOS' },
    { name: 'Carlos', course: 'Web' },
  ])
)

// oputcome
// {
//   Blockchain: ["Brendan"],
//   Web: ["David", "Carlos"],
//   iOS: ["Martha"]
// }

JS Object From URL Encoded String

In this exercise, we will be given a url encoded string of key-value pairs, and we will have to turn it into a JavaScript object.

To safely send data in a URL, the data first has to be encoded to convert any special characters to URL safe characters. For this assignment we will only focus on the following URL encoding rules:

  • %20 represents a space character.
  • Key-value pairs are represented using an = character: key=value
  • Multiple key-value pairs are separated using a & character: key1=value1&key2=value2

So the following URL encoded string:

city=Vancouver&weather=lots%20of%20rain

Could be converted to the following JavaScript object:

{
  city: "Vancouver",
  weather: "lots of rain"
}

Example:

const urlDecode = function (text) {
  // Put your solution here
}

console.log(urlDecode('duck=rubber')) // { duck: "rubber" }
console.log(urlDecode('breed=cattle%20dog')) // { breed: "cattle dog" }
console.log(urlDecode('city=Vancouver&weather=lots%20of%20rain')) // { city: "Vancouver", weather: "lots of rain" }
console.log(urlDecode('city=Vancouver&weather=lots%20of%20rain').weather) // "lots of rain"

Possible Solution:

const urlDecode = function (text) {
  let result = {}
  let arrOfKeys = text.split('&')

  for (let message of arrOfKeys) {
    result[message.substr(0, message.indexOf('='))] = message
      .substr(message.indexOf('=') + 1)
      .replace(/[%20]/g, ' ')
  }
  return result
}

console.log(urlDecode('duck=rubber')) // { duck: "rubber" }
console.log(urlDecode('breed=cattle%20dog')) // { breed: "cattle dog" }
console.log(urlDecode('city=Vancouver&weather=lots%20of%20rain')) // { city: "Vancouver", weather: "lots of rain" }
console.log(urlDecode('city=Vancouver&weather=lots%20of%20rain').weather) // "lots of rain"

Taxicab Geometry

In this exercise, we will write an algorithm to help taxicabs determine how far away a destination is based on the directions given.

The following grid represents the streets of a city and the blue dot represents a taxi cab before it leaves for its destination.

taxicab geometry

The taxi driver is given a list of directions that tell her whether to turn left or right, and how many blocks to drive for. Every time the taxi driver has to turn left, she will make a 90° turn anticlockwise, and every time the taxi driver has to turn right, she will make a 90° turn clockwise.

Let's take a look at some example directions: ["right", 2, "left", 3, "left", 1].

First the taxi driver must turn "right", then drive for 2 blocks.

taxicab geometry

Then the taxi driver must turn "left", and drive for 3 blocks.

taxicab geometry

Finally, the taxi driver must turn "left" again, and drive for 1 block.

taxicab geometry

Now that the taxi driver is at her final destination, we can determine that she is 1 block east and 3 blocks north of where she started.

Examples:

const blocksAway = function (directions) {
  // Put your solution here
}

console.log(blocksAway(['right', 2, 'left', 3, 'left', 1])) // {east: 1, north: 3}
console.log(blocksAway(['left', 1, 'right', 1, 'left', 1, 'right', 1, 'left', 1, 'right', 1])) // {east: 3, north: 3}
console.log(blocksAway(['left', 3, 'right', 1, 'right', 3, 'right', 1])) // {east: 0, north: 0}

Create a function named blocksAway() that will receive an array of directions, and return an object that calculates how far north and east those directions will take someone.

The taxi driver will always start at the same position, in the most south west position on the grid. This means that the output will only need to specify an east and north position, since the taxi driver can only end up in these East and North of the starting point.

Possible Solution:

const blocksAway = function (directions) {
  let finalLocation = {
    east: 0,
    north: 0,
  }

  let degrees = 0

  for (let i = 0; i < directions.length; i += 2) {
    if (directions[i] === 'right') {
      if (degrees === 0 && i === 0) {
        degrees += 0
        finalLocation.east += directions[i + 1]
      } else if (degrees === 0) {
        degrees += 270
        finalLocation.north -= directions[i + 1]
      } else if (degrees === 90) {
        degrees -= 90
        finalLocation.east += directions[i + 1]
      } else if (degrees === 180) {
        degrees -= 90
        finalLocation.north += directions[i + 1]
      } else if (degrees === 270) {
        degrees -= 90
        finalLocation.east -= directions[i + 1]
      }
    }
    if (directions[i] === 'left') {
      if (degrees === 0) {
        degrees += 90
        finalLocation.north += directions[i + 1]
      } else if (degrees === 90) {
        degrees += 90
        finalLocation.east -= directions[i + 1]
      } else if (degrees === 180) {
        degrees += 90
        finalLocation.north -= directions[i + 1]
      } else if (degrees === 270) {
        degrees -= 270
        finalLocation.north += directions[i + 1]
      }
    }
  }

  return finalLocation
}

console.log(blocksAway(['right', 2, 'left', 3, 'left', 1])) // {east: 1, north: 3}
console.log(blocksAway(['left', 1, 'right', 1, 'left', 1, 'right', 1, 'left', 1, 'right', 1])) // {east: 3, north: 3}
console.log(blocksAway(['left', 3, 'right', 1, 'right', 3, 'right', 1])) // {east: 0, north: 0}