Calvin Goodale, Developer - Blog
c g o o d a a l e . . c o m

Creating a Navigation Drop-down in React

For an MVP I'm currently working on, the client wanted some of the top navigation menu items to have drop-downs containing other nagivation links. I'm sure there are many ways to implement this, but this is how I decided to do it:

  • Create a NavDropdown component that takes dropdownItems props and maps them to NavLinks:

    NavDropdown component:

    import { NavLink } from 'react-router-dom';
    
    function NavDropdown({dropdownItems}) {
      return (
        <div className='nav-dropdown'>
          {
            dropdownItems.map(item => {
              return <NavLink className='nav-dropdown-item' to=`/${item}`>{item}</NavLink>
            })
          }
        </div>
      );
    }
    
    export default NavDropdown;
  • Import the NavDropdown component into the NavBar component, and pass it the appropriate text.
  • Use onMouseEnter and onMouseLeave events to set the hoveredItem state in the NavBar component, and conditionally render the NavDropdown component based on this state.

    NavBar component:
    import { useState } from 'react';
    import { NavLink } from 'react-router-dom';
    
    import NavDropdown from './NavDropdown';
    
    function NavBar() {
    
      const [hoveredItem, setHoveredItem] = useState('')
    
      return (
        <div id='navbar' className='relative mx-4 flex'>
          <NavLink className='top-nav-item' to='/'>Home</NavLink>
          <NavLink className='top-nav-item' onMouseEnter={() => setHoveredItem('about')} onMouseLeave={() => setHoveredItem('')} to='/about'>
            About
            {hoveredItem === 'about' &&
              <NavDropdown dropdownItems={['Link1', 'Link2', 'Link3']}/>
            }
          </NavLink>
          <NavLink className='top-nav-item' to='/signup'>SignUp</NavLink>
        </div>
      );
    }
    
    export default NavBar;

I still have a few things to clean up, but I'm pretty happy with how it functions, and thanks to conditional rendering I didn't have to use any CSS display property nonsense.


0 Comments

Advent Of Code 2023 - Day 4

The Day 4 puzzle involved determining the point value of scratch cards.

Part 1
Each line of the input was a series of 5 winning numbers, followed by the 7 numbers on the scratch card. The first winning number is worth 1 point, and each winning number after that doubles the amount of points won.

Example:

Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19

Card 1 has 4 winning numbers, giving you a running total of 8 points. Card 2 has 2 winning numbers, for a score of 2 points, making the total points won equal to 10.

Part 1 was easy enough after the data was parsed. I looped through each series of card numbers while keeping a variable score. If the score was null, I made it 1, otherwise I doubled it. These scores were added to a totalScore variable, giving me the answer once all the points had been calculated.

Part 2
The second part of the puzzle added some complications. Now, instead of  points, each winning number on a card gives you copies of the cards below it. For Card 1 of the above example you have 4 winning numbers, which would give you copies of the next 4 cards in the input.

To solve this part, I first created an object of key value pairs, with the key being the number associated with the card, and the value being the number of wins that card has. I also created a second key value object with the count of cards, starting at 1. (I probably could have kept them in the same object, but decided to separate them so I could better keep track of what was happening).

I then looped over the keys of of the first object, got the number of wins that card has, then created a loop from (card number + 1) to (cardnumber + 1 + numberOfWins) and incremented the count of each of those cards.

They key here is that you can never go backwards - i.e. you can only win copies of the cards after each card, so you could simply add card counts to the copies of the won cards and you would still be able to hit them all in a single for loop. 

Solution here:
https://github.com/cwg83/AdventOfCode2023/blob/main/Day4/index.js


0 Comments

Advent Of Code 2023 - Day 3

Part 1
Day 3 felt a bit more involved than the previous days. The challenge was to locate "part numbers" in the given input, which in this context is determined by whether or not the number is adjacent to a symbol (any non integer character that is not a period) when viewing the input as a matrix.

Example:

467..114..
...*......
..35..633.

467 and 35 are part numbers because they are adjacent to the * character (i.e. any index of the number has a symbol at an adjacent index, including diagonals).

To solve this part, I decided to loop over each line of input until I get to an string representation of an integer. I used the same helper function I used on Day 1 to determine if a given character can be parsed into an integer:

const isNumeric = (n) => {
  return !isNaN(parseFloat(n)) && isFinite(n);
};

Upon reaching an integer character, I started another loop that moved along the column until I reached a non integer character and checked the 8 adjacent indexes for non-period and non-integer characters. If I found one, I added the number to my partsNumSum variable since the puzzle answer in this case was the total sum of all part numbers.

Part 2
For Part 2, we were told that a 'gear' is represented by any 2 part numbers that are both adjacent to the same '*' symbol. The 'gear ratio' is the two part numbers in the gear multiplied together. The answer we needed to find was the sum of all 'gear ratios' in the matrix.

To solve this, I added some code that checked if the symbol adjacent to a part number was a '*', and if so I added a count of the string representation of the row and col indices to an object I called gearRatios (if the row col string exists in gearRatios then increment, otherwise set it to 0), which was my semi-hacky way of making sure I'm not adding duplicate row col combinations.

I then looped through this object and calculated the gear ratios and returned the final sum.

Although I know I have some inefficiencies in my code particularly for this day, I typically view the Advent of Code puzzles as an MVP solution rather than trying to find the optimal way of solving them, not only because we are scored based on how fast we find the solution, but also because I just don't have time to spend more than an hour or two on each puzzle.

Solution here:
https://github.com/cwg83/AdventOfCode2023/blob/main/Day3/index.js


0 Comments

Advent Of Code 2023 - Day 2

Part 1
Given a text string with each line representing a multi-round game (rounds are separated by semicolons) where a number of colored cube are pulled from a bag, determine if each game is possible if the bag contained only 12 red cubes, 13 green cubes, and 14 blue cubes.

Given examples:
Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Possible - the number of cubes pulled in each round for each color never goes above the max # of cubes for that color.

Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Impossible - In round 1, 20 red cubes are pulled, which is more than the limit of 12 red cubes.

As usual with these Advent of Code puzzles, the first step is to parse the puzzle input string into a usable format. I decided to create a gamesDict object in which I would store game data, with a structure like this:

'Game 1': {
    '1': { blue: 12, red: 15, green: 2 },
    '2': { red: 17, green: 8, blue: 5 },
    '3': { red: 8, blue: 17 },
    '4': { green: 9, blue: 1, red: 4 }
  },
  'Game 2': {
    '1': { red: 6, blue: 6, green: 2 },
    '2': { blue: 1, red: 1 },
    '3': { green: 6, red: 1, blue: 10 }
  }
  // etc

I think iterated over each Game ID, iterated over each round #, and kept a count of each color pulled in each respective round. If the color pulled in that round was more than the max for that color, I set a gamePossible variable to false. At the end of each game loop, if gamePossible was true, I added that Game ID (parsed to an Int) to an array. I then used reduce to sum the IDs in this array.

Part 2
Today's Part 2 felt easier than Day 1. The challenge here was to determine the fewest number of cubes in each color necessary for the games to be possible, and then calculate what they called the "Powerset" of those numbers, which is calculated by gettin the minimum number of cubes for each color (red, green, blue) for each game and multiplying those numbers together, with the final answer to the puzzle being the sum of all of these Powersets.

I was able to use my code from Part 1 and add an object to keep track of the minimum number of each color for that game, which was just the max count of that color in all of the rounds, and push the results to an array:

// Part 2
var allMaxCounts = [];
for (var gameID of Object.keys(gamesDict)) {
   roundMaxCounts = {
      red: 0,
      green: 0,
      blue: 0,
   };
   for (var roundNum of Object.keys(gamesDict[gameID])) {
      var roundData = gamesDict[gameID][roundNum];
      for (var roundColor of Object.keys(roundData)) {
         if (roundData[roundColor] > roundMaxCounts[roundColor]) {
            roundMaxCounts[roundColor] = roundData[roundColor];
         }
      }
   }
   allMaxCounts.push(roundMaxCounts);
}

From there it was trivial to calculate the Powersets and sum them together:

var totalPowerSet = 0;
for (var i = 0; i < allMaxCounts.length; i++) {
   var powerSet = allMaxCounts[i].red * allMaxCounts[i].green * allMaxCounts[i].blue;
   totalPowerSet += powerSet;
}


Full code here:
https://github.com/cwg83/AdventOfCode2023/blob/main/Day2/index.js






0 Comments

Advent Of Code 2023 - Day 1

I decided again to try my hand at the annual Advent of Code challenge. For the unitiated, Advent of Code is an "advent calendar of small programming puzzles" created by Eric Wastl in 2015. Every day in December, a new two-part programming challenge is released at midnight. You can compete against players on the global leaderboard, or create a private leaderboard for your friends to compete in.

The puzzles typically get progressively more difficult each day. For each half of the puzzle, you are provided instructions, a sample puzzle input, an actual puzzle input, and a field for you to enter your answer. If you get the correct answer for part 1, you are given the instructions and inputs for part 2, which typically are built on challenge / scenario given in the first part.

Compared to the types of coding challenges found on websites such as LeetCode or HackerRank, the puzzles on Advent of Code seem to (in my opinion) be slightly more representative of challenges you might come across working as a Software Engineer. Most of them involve sanitizing and collecting data from a block of text input which you will then parse and use to find the answer using the given instructions.

Day 1 - https://adventofcode.com/2023/day/1

Part 1
Part 1 involved parsing out lines of text and locating the first and last string-version of digits within that string. A couple of given examples:

1abc2 -> first digit = 1, last digit = 2
pqr3stu8vwx -> first digit = 3, last digit  = 8

Once you've found each of the first and last digits in a string, you combine the strings together to get a number, so in the above examples the numbers for each respecitve line would be 12 and 38. You then need to add each number found in each line of text together to get your final answer. So in the example using the 2 lines above, the answer would be 50 (12 + 38).

My first thought was that you would simply need to create two variables num1 and num2, loop over each character in the string, determine if the character is the string representation of an integer, and if so set num1 and num2 to the first and last you find. Of course, you needed a way to figure out if a character is a string-representation of an integer first, so that was my first task.

After some research, I found that using the parseFloat() function on a character and making sure it a number as well as using the isFinite() function on the character to make sure it isn't Infinity is a decent way of doing so:

const isNumeric = (n) => {
  return !isNaN(parseFloat(n)) && isFinite(n);
};

I then iterated over each character and checked if it could be parsed into an integer while keeping track of the first and last instance. This allowed me to solve Part 1 without too much difficulty, but of course Part 2 always throws some wrenches in your gears.

Part 2
In this case, the wrench thrown at me (by elves, strangely enough) was that the new puzzle input could contain integers represented by words! 

Given examples:

abcone2threexyz -> first digit = 1 ("one"), last digit = 3 ("three)
xtwone3four -> first digit = 2 ("two"), last digit = 4 ("four")

This means that I not only had to iterate over each character and check if the character itself could be parsed into a digit, but I also had to check for the word version of each of the digits one through nine. As I had been learning about Tries recently (which apparently is pronounced like "tree" but I refuse to do so), I figured that this would be a good opportunity to practice using a Trie and would also be an efficient way of checking for word versions of integers.

I created an object of word -> integer key value pairs, and then built a Trie out of them, and then in the same loop I was using for Part 1 I added a second if statement:
If the character could NOT be parsed into an integer, I kept a pointer j starting at the next index and checked both if the string from i to j was a prefix in my Trie and if it was a key in my Trie. If at any time it was not a prefix (or I reached a character that could be parsed into an integer itself), I could break out of the loop. If the word was found in the Trie, I retrieved the value from my word -> integer dictionary and used that as the digit.

The second part took me a lot longer than the first, as it took me a little while to figure out where and when I could break out of the loop, but I was eventually able to find the answer.

My full code can be found here:
https://github.com/cwg83/AdventOfCode2023/blob/main/Day1/index.js

I just finished Day 2 a few hours ago, so I'll be adding a blog post for that one soon.

Merry coding!

 

 

 

 


0 Comments