It appears that I may have received the wrong beverage from the Starbucks barista. I had requested a lavender oat milk latte but received a matcha lavender oat milk latte instead. However, I am content with the beverage, which is quite enjoyable. Perhaps fate has deemed today a non-coffee day, or could this be a playful April Fool’s prank? I kind of doubt that it’s a prank, just maybe a new cashier that put in the wrong order.
I’ve returned to the University library, which seems quite crowded today. Perhaps it’s due to the rainy weather or just the start of a new week. Interestingly, while we’re experiencing snow in the mountains, it’s only rain here in the city. I prefer to sit in the back of the library, where I can keep an eye on my surroundings, which helps to alleviate my anxiety. Although I’m a bit uneasy with someone sitting behind me on the bench by the window, I’m reminding myself there’s no reason to feel threatened. I can handle this situation.
Over the weekend, we had smash burgers on the new griddle. It was fun. We had the fire pit going, which gave the sensation of being out camping, albeit without the tents. The following day, Tommy cooked up breakfast on the griddle. It felt like a restaurant, especially with as much food as we made. I tried my skills on the griddle by cooking my eggs. It wasn’t as effortless as expected, and mastering the griddle will take some time and practice. Later, I helped Tommy clean the garage. The garage is slowly coming together.
Today’s coding session was productive. Although there were some moments of confusion, I overcame them and gained a better understanding. My concentration level was impressive, even with many people in the library. I didn’t find their conversations distracting since I couldn’t hear them.
We are currently experiencing thunder, lightning, and hail. The library is dark because most of the light comes from the windows. Inclement weather is expected today and tomorrow.
I’m going to get some reading in before Tommy picks me up. I must finish The Isles of the Gods. Then, Tommy and I will head to Costco before coming home.
Some pictures from this weekend:
1) New griddle. Sautéed onions for smash burgers.
2) New table for prepping.
3) Karissa, Kel, Alex and Jay hanging out by the fire pit. Chris was inside.
4) Merlin getting a bone.
JavaScript notes…
———————————–
Spreadsheet steps 51 – 70
Step 51
You can pass a function reference as a callback parameter. A function reference is a function name without the parentheses. For example:
const myFunc = (val) => `value: ${val}`;
const array = [1, 2, 3];
const newArray = array.map(myFunc);
The .map() method here will call the myFunc function, passing the same arguments that a .map() callback takes. The first argument is the value of the array at the current iteration, so newArray would be [value: 1, value: 2, value: 3].
Pass a reference to your elemValue function as the callback to your .map() method.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue); }
Step 52
Because elemValue returns a function, your addCharacters function ultimately returns an array of function references. You want the .map() method to run the inner function of your elemValue function, which means you need to call elemValue instead of reference it. Pass num as the argument to your elemValue function.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); }
Step 53
Declare a rangeExpanded variable and assign it the result of calling the .replace() method of your x parameter. Pass the rangeRegex variable as the argument.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex); }
Step 54
The second argument to the .replace() method does not have to be a string. You can instead pass a callback function to run more complex logic on the matched string.
The callback function takes a few parameters. The first is the matched string. Pass an empty callback function to your .replace() call, and give it a match parameter.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex, (match) => { }); }
Step 55
The callback function then has a parameter for each capture group in the regular expression. In your case, rangeRegex has four capture groups: the first letter, the first numbers, the second letter, and the second numbers.
Give your callback function four more parameters to match those capture groups: char1, num1, char2, and num2. char will be short for character.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex, (match, char1, num1, char2, num2) => {}); }
Step 56
Have your callback implicitly return the result of calling rangeFromString() with num1 and num2 as the arguments.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex, (match, char1, num1, char2, num2) => rangeFromString(num1, num2)); }
Step 57
Call the .map() method on your rangeFromString() call, passing a reference to addCharacters as the callback function.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex, (match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters)); }
Step 58
addCharacters returns a function, so you’ll want to call it. Pass char1 as the argument.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex, (match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1))); }
Step 59
Your addCharacters(char1) is also returning a function, which returns another function. You need to make another function call to access that innermost function reference for the .map() callback. JavaScript allows you to immediately invoke returned functions:
myFunc(1)(“hi”);
Immediately invoke the function returned from your addCharacters(char1) call, and pass char2 as the argument.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex, (match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2))); }
Step 60
Now that your .map() function is receiving the innermost function reference from addCharacters, it will properly iterate over the elements and pass each element as n to that function.
You’ll notice that you are not using your match parameter. In JavaScript, it is common convention to prefix an unused parameter with an underscore _. You could also leave the parameter empty like so: (, char1) but it is often clearer to name the parameter for future readability.
Prefix your match parameter with an underscore.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2))); }
Step 61
Declare a variable cellRegex to match cell references. It should match a letter from A to J, followed by a digit from 1 to 9, and an optional digit from 0 to 9. Make the regular expression case-insensitive and global.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2))); const cellRegex = /[A-J][1-9][0-9]?/gi; }
Step 62
Declare a cellExpanded variable and assign it the value of calling .replace() on your rangeExpanded variable. Pass it your cellRegex and an empty callback function. The callback function should take a match parameter.
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2))); const cellRegex = /[A-J][1-9][0-9]?/gi; const cellExpanded = rangeExpanded.replace(cellRegex, (match) => {}) }
Step 63
Update your callback function to return the result of calling idToText() with match as the argument. Remember that your regular expression is case-insensitive, so you will need to call toUpperCase() on match before passing it to idToText().
const evalFormula = (x, cells) => { const idToText = id => cells.find(cell => cell.id === id).value; const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi; const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2)); const elemValue = num => character => idToText(character + num); const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num)); const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2))); const cellRegex = /[A-J][1-9][0-9]?/gi; const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase())) }
Step 64
In mathematics, an infix is a mathematical operator that appears between its two operands. For example, 1 + 2 is an infix expression.
To parse these expressions, you will need to map the symbols to relevant functions. Declare an infixToFunction variable, and assign it an empty object.
const infixToFunction = { }
Step 65
Object values do not have to be primitive types, like a string or a number. They can also be functions.
Give your infixToFunction object a + property. That property should be a function that takes an x and y parameter and implicitly returns the sum of those two parameters.
Because + is not alphanumeric, you’ll need to wrap it in quotes for your property.
const infixToFunction = { "+": (x, y) => x + y, }
Step 66
Now create a – property that is a function that takes an x and y parameter and implicitly returns the result of subtracting y from x.
const infixToFunction = { "+": (x, y) => x + y, "-": (x, y) => x - y, }
Step 67
Following the same pattern, add a property for multiplication * and division / with the appropriate functions.
const infixToFunction = { "+": (x, y) => x + y, "-": (x, y) => x - y, "*": (x, y) => x * y, "/": (x, y) => x / y }
Step 68
Now that you have your infix functions, you need a way to evaluate them. Declare an infixEval function which takes two parameters, str and regex. It should implicitly return the .replace() method of str, with regex and an empty callback as the arguments.
const infixEval= (str, regex) => str.replace(regex, () => {})
Step 69
Your callback needs four parameters. match, arg1, operator, and arg2.
You will not be using the match parameter, so remember to prefix it.
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => {});
Step 70
The regex you will be passing to your infixEval function will match two numbers with an operator between them. The first number will be assigned to arg1 in the callback, the second to arg2, and the operator to operator.
Have your callback function implicitly return the operator property of your infixToFunction object. Remember that operator is a variable which holds the property name, not the actual property name.
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator]);