Christian.

Challenge

April 15, 2021

I recap of coding challeneges I had.

01 - Javascript: Fetch Movie Data

Prompt: Given a year in the format YYYY, make a GET call to the API to get all the movies for this year. You cannot use the axios library or the fetch API.

Being a self-taught developer, I’ve come to my education by starting with the “latest & greatest” so I’d only ever fetched data via axios and fetch so I enjoyed learning the “vanilla JS” way of doing it with a Promise and the XMLHttpRequest web API.

First of all we set up our url we’re calling. I added string interpolation so that we can pass it a year. Then I set up an empty array where we’ll push our data to.

const url = `https://jsonmock.hackerrank.com/api/movies?Year=${year}`

const promiseArray = []

We create the new (XMLHttpRequest) request. We call it’s .open() method and pass it the type of request we want (“GET”) and the url we’re requesting data from. Next, the .onload() function will be called after the request is successful. We parse the response data and turn it into JSON, and then we assign that JSON object to our results variable. Lastly, we use Promise.resolve() that will “fulfill” the Promise when the data we pass it (request.response), is returned.

const request = new XMLHttpRequest()

request.open("GET", url)

request.onload = function() {
  if (request.status === 200) {
    const results = JSON.parse(this.response)

    resolve(request.response)

This bit checks if there’s less than 1 data item in the array. We then return the empty promiseArray.

var length = results.data.length

if (length < 1) {
  console.error(`ERROR: No results found for ${year}'s query!`)

  return promiseArray
}

This part is what displays our data in the DOM. I added a header to show the user which year we’re dealing with below, and then I iterate over the length of the movie data (length of results.data). So we iterate over each item (movie) and then do two things. We first push it (the Title of the data) to our promiseArray to store the data. Next, we use document.write() to display an <li> with each movie title in the DOM.

document.write(`<h1>Movies from the ${year}</h1>`)

for (i = 0; i <= length - 1; i++) {
  promiseArray.push(results.data[i].Title)

  document.write(`
    <ul>
      <li key=${results.data[i].imdbID}>${results.data[i].Title}</li>
    </ul>`)
}

This is the error handling portion. A call can be made successfully, but if the data array is empty it’s technically an error, for our purposes. We want to return a list of movies, so even though it successfully calls, it can’t fetch data that’s not there so we need to let the user know that “No Results Found”. Underneath that we .send() the request. This is the line of code that actually runs the function and sends the request to the server.

    } else {
      reject(Error(request.statusText))
    }
  }

  request.onerror = function() {
    new Error("No Results Found")
  }
  request.send()
})
}

Lastly, I called the function 3 times with different years to test several things. I wanted to test that it’ll return the data in order even with multiple calls. Also, I knew the 2020 call would return no data so I wanted to make sure it would throw an error, even when a successful call is being made. Or you could just comment out either and test them one at a time.

getMovies(2010)
getMovies(2012)
getMovies(2020)

Full Code

function getMovies(year) {
  const url = `https://jsonmock.hackerrank.com/api/movies?Year=${year}`

  const promiseArray = []

  return new Promise(function(resolve, reject) {
    const request = new XMLHttpRequest()

    request.open("GET", url)

    request.onload = function() {
      if (request.status === 200) {
        const results = JSON.parse(this.response)

        resolve(request.response)

        var length = results.data.length

        if (length < 1) {
          console.error(`ERROR: No results found for ${year}'s query!`)

          return promiseArray
        } else {
          document.write(`<h1>Movies from the ${year}</h1>`)

          for (i = 0; i <= length - 1; i++) {
            promiseArray.push(results.data[i].Title)

            document.write(`
                <ul>
                  <li key=${results.data[i].imdbID}>${results.data[i].Title}</li>
                </ul>`)
          }
        }
      } else {
        reject(Error(request.statusText))
      }
    }

    request.onerror = function() {
      new Error("No Results Found")
    }
    request.send()
  })
}
getMovies(2010)
getMovies(2012)
getMovies(2020)

02 - Python: Word Histogram

Prompt: Create a histogram of words from given statement.

So by histogram, basically what we want is a frequency counter. We want to parse the sentence and return a list of each word and the number of times that word was “seen” next to it. For instance if the word "the" occurs twice in the sentence our program should print out the : 2. The prompt also asked us to add that color (:) between the word and the occurance count.

So we start with our given sentence and in our function I created an empty dictionary to store our key:value (word:count) pairs. I started by calling .split() on the sentence, which splits the sentence up by word, and it’ll ignore the newline chracters \n so this is a simple way to divvy up the sentence by actual word.

sentence = "This line is not the end.\nThis line will be the last!\nOne more thing Tulip is dog-friendly.

def histrogram(sentence):
  d = {}

  sentence = sentence.split()

This is the core logic of our hash map. Basically we’re iterating over our list of words in our sentence and for word (w) in sentence we’ll assess it and do one of two things. If we don’t have this word in our dictionary than we’ll add it. That’s the else case. If we have the word (w) in our dictionary (d) than we’ll add 1 to our count of said word, meaning we’ve seen it a consecutive time. This will keep a running count of the occurence of each word in the sentence.

for w in sentence:
  if w in d:
    d[w] += 1
  else:
    d[w] = 1

The individual WORDS are our keys and the frequency COUNT is the value in our key:value pairs. The d.items() just allows us to iterate of the pairs. So when we say for k, v we’re saying “for keys & values in our dictionary…” we want to print the value of k (key), which will be the literal word in sentence and the concat a space, a colon, and then another space while finally print the count. We convert the number (type int) to a string so we can print it to the console.

Lastly, outside the function definition, we call the function and pass the sentence into it.

  for k, v in d.items():
    print(k + " : " + str(v))

histrogram(sentence)

Full Code

sentence = "This line is not the end.\nThis line will be the last!\nOne more thing Tulip is dog-friendly."

def histrogram(sentence):
  d = {}

  sentence = sentence.split()

  for w in sentence:
    if w in d:
      d[w] += 1
    else:
      d[w] = 1

  for k, v in d.items():
    print(k + " : " + str(v))


histrogram(sentence)

03 - React: Username Validation

Prompt: Create input box, user adds a username that must be minimum of 4 characters long and unique. To verify it’s unique, I had to hit an API that checked it against their database and returned true vs. false.

My full answer is at the bottom, but I wanted to break down my thoughts as well.

After setting up the project locally, via git, I upgraded the React & React DOM version from v16.x to v17 so that I could use Hooks. I then changed the class component to a functional one and imported { useState }. Then I set up the state I would need. I knew I would need the username that the user would input. I also knew I would need the state of whether a username was taken or not. I called it taken and I knew we’d be getting back a boolean value, true or false. I set the state of each to an empty string.

const [taken, setTaken] = useState("")
const [username, setUsername] = useState("")

Next, I created the form in JSX. I started with a wrapped div with a simple margin style prop so I can visually see better and not smushed up against the corners of the DOM. So the form will have a handleSubmit method. Then I created a label for some instructions. Then two input fields. One input field of and another button. The first input field had the standed type=text to define what input we’re expecting, and I bound the value of the field to the username from our useState hook. Then, the onChange method that has had the setUsername useState function to take the input value the user inputs and we’re setting that equal to our the state of username. Lastly, the submit input is type=submit to define its a button type and that’s it.

<div
  style={{
    margin: 20,
  }}
>
  <form onSubmit={handleSubmit}>
    <br />
    <label>
      Input Username:{" "}
      <input
        type="text"
        placeholder="enter username"
        value={username}
        onChange={e => {
          setUsername(e.target.value)
        }}
      />
    </label>

    <input type="submit" value="Submit" />
  </form>
</div>

So to the meat. Our handleSubmit function is where “everything” happens. First, we call preventDefault on our e (event). Since forms, by default, submit POST requests, they will reload the page, but we don’t use a form for this purpose. Therefore, we want to prevent it’s default behavior. We’ll use our form button to fetch data and therefore don’t want our page to reload. Moving on, we do our username validation. The requirements were to check if the username was at least 4 characters long. So we’re error checking for the opposite. So, with our conditional operator, we say “if there’s a username AND said length username is LESS THAN 4 (characters) then we’ll console.error our descriptive error message letting our user know that said username doesn’t meet the given criteria.

function handleSubmit(e) {
  e.preventDefault()

  if (username && username.length < 4) {
    console.error("ERROR:Username must be at least 4 characters long")
  }

Within our handleSubmit function we have an asynchronous fetch function that waits for data back from our API. I used string interpolation to dynamically call the API with whatever the state/value of our username is (i.e. the username the user input).

We then await our response and call .json() which we use to extract a JSON object from the response data. This returns a Javascript Promise so that’s why we have to await it. I then clean up the code by creating a variable for the nested data and then I assign the boolen value to a string. We’re ultimately returning true or false so to display it, it must be a string. I call the Javscript method .toString() on the boolean value and then assign it to state using our setTaken() hook.

async function fetchData() {
  const response = await fetch(
    `https://hxj1tck8l1.execute-api.us-east-1.amazonaws.com/default/users/taken?username=${username}`
  )

  const data = await response.json()
  const result = data.taken

  const strResult = result.toString()

  console.log(strResult)
  setTaken(strResult)
}

Then, below the function body, we call our async fetchData function, and then we’ll reset our username value in the input field back to an empty string. This does two things, one it will visually reset the field value in the DOM which indicates to the user that they can search again, and secondly, it resets the value in our hook to be ready to receive a new value upon next submit.

fetchData()
setUsername("")

Below our form, I added this JSX element. It does two things. This is a conditional rendering block. So it will check for the state of taken (i.e. Do we have taken state? Is taken truthy?). So IF taken is true THEN (that’s what the && is doing, conditionally checking for the left side to then render the right side) display the taken state in an <h3> tag. This will display “True” if the username that the username IS TAKEN (i.e. not valid) or “False” if the username is not taken (i.e. free to use).

{
  taken && <h3>{taken}</h3>
}

Full Code

import React, { useState } from "react"

export default function App() {
  const [taken, setTaken] = useState("")
  const [username, setUsername] = useState("")

  function handleSubmit(e) {
    e.preventDefault()

    if (username && username.length < 4) {
      console.error("ERROR:Username must be at least 4 characters long")
    }

    async function fetchData() {
      const response = await fetch(
        `https://hxj1tck8l1.execute-api.us-east-1.amazonaws.com/default/users/taken?username=${username}`
      )

      const data = await response.json()
      const result = data.taken

      const strResult = result.toString()

      console.log(strResult)
      setTaken(strResult)
    }

    fetchData()
    setUsername("")
  }

  return (
    <div
      style={{
        margin: 20,
      }}
    >
      <form onSubmit={handleSubmit}>
        <br />
        <label>
          Input Username:{" "}
          <input
            type="text"
            placeholder="enter username"
            value={username}
            onChange={e => {
              setUsername(e.target.value)
            }}
          />
        </label>

        <input type="submit" value="Submit" />
      </form>

      {taken && <h3>{taken}</h3>}
    </div>
  )
}

04 - Vue: Search By Ingredient

Prompt: Create a way to filter the pizzas by topping.

The basic overview here is that I was given a program that fetched data from a local .json file and rendered it in the broswer. First of all, I was asked to fetch & display all the data from a second .json file, which was nested differently. I did that pretty quickly, but I didn’t add that code here. After, I went back to the initial pizza template file and then create a search form.

So I created the form tag with an input field and a button. The form is where we call our submit function. The input has a v-model directive so that we can bind the input value with our data() value. The button is type=submit and we’ll use that to call the filter function we’ll create next.

<form @submit.prevent="filterPizza">
  <input type="text" placeholder="ex. Bacon" v-model="pizzaTopping" />
  <button type="submit">Search</button>
</form>

Next, we’ll add a searchTopping and a pizzaTopping data property in our Vue data() method. We’ll initially set them to an empty string. The pizzaTopping is what the user will type into the form field. It’s the “request” and the searchTopping is what we’ll use to filter. More on that later.

data() {
  return {
    searchTopping: "",
    pizzaTopping: "",
    pizzas: [],
  }
}

In our Vue template (HTML), I added a v-if directive. At first, we were displaying ALL the pizzas in the .json file, but since we only the pizzas that have the ingredient we’re searching for, we’ll add this v-if condition to only render the relevant pizzas. We query the data to check the toppings array in the JSON object with pizza.toppings. and then we use the Javascript array method .includes() and pass it the searchTopping. So basically we are only rendering pizzas IF said pizza’s array of toppings contains the exact topping the user input.

<div
  class="panel panel-default"
  v-for="pizza in pizzas"
  v-bind="pizza"
  :key="pizza.name"
  v-if="pizza.toppings.includes(searchTopping)"
></div>

The filterPizza() function first checks exactly what we just said above. We need to only return when the user inputted query (pizzaTopping) equals the pizza’s JSON data (searchTopping). Then, we reset pizzaTopping to an empty string, therefore resetting the input field in the DOM. We have to use two different strings, and then comparing them within the function, because if we just passed the one string then the v-model would constantly assess for equality between its value and the JSON data and therefore filter AS WE TYPE. However, the prompt asked that we filter ON CLICK/SUBMIT.

filterPizza() {
  this.searchTopping = this.pizzaTopping
  this.pizzaTopping = ""
}

Full Code

<template>
  <div>
    <h1>Pizzas!</h1>

    <form @submit.prevent="filterPizza">
      <input type="text" placeholder="ex. Bacon" v-model="pizzaTopping" />
      <button type="submit">Search</button>
    </form>

    <br />

    <div
      class="panel panel-default"
      v-for="pizza in pizzas"
      v-bind="pizza"
      :key="pizza.name"
      v-if="pizza.toppings.includes(searchTopping)"
    >
      <div class="panel-heading">
        <h3 class="panel-title">{{ pizza.name }}</h3>
      </div>
      <div class="panel-body">
        <h4>Toppings</h4>
        <ul>
          <li v-for="topping in pizza.toppings" :key="topping">
            {{ topping }}
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: "PizzaList",
    data() {
      return {
        searchTopping: "",
        pizzaTopping: "",
        pizzas: [],
      }
    },
    created() {
      fetch("/pizzas.json")
        .then(response => response.json())
        .then(pizzas => (this.pizzas = pizzas))
    },
    methods: {
      filterPizza() {
        this.searchTopping = this.pizzaTopping
        console.log(this.pizzaTopping)
        this.pizzaTopping = ""
      },
    },
  }
</script>

Written by Christian Turner
Follow me on Twitter