CodingNic

Practical Challenges

Data Shape Challenges

Practical Challenges 70 min read

Data Shape Challenges

Objectives

This chapter introduces no new concepts. These are the largest challenges in the course: arrays of objects, nested arrays, and comparing data shapes, pulling together everything from Modules 1 through 9.

Challenge 1: Sum Every Value Across an Array of Objects

Write a function calculateMonthlyOrders(records) that accepts an array of objects. Each object has one or more properties, one per month, with a numeric order count. The function should return the sum of every numeric value in every object combined.

Example:

javascript
calculateMonthlyOrders([
  { February: 1, March: 2, April: 2 },
  { April: 1, May: 2, June: 2 }
]); // 10

calculateMonthlyOrders([
  { Jan: 100, Feb: 200 },
  { Mar: 50 }
]); // 350

Hint: Object.values() turns an object into an array of just its values, and you need to add those up for every object in the outer array. Nesting a .reduce() inside another .reduce() (or a .forEach()) handles both layers.

Challenge 2: Collect Odds and Evens

Write a function collectOddsAndEvens(nums) that accepts an array of positive integers and returns an object counting how many are odd and how many are even, under the keys "odd" and "even".

Example:

javascript
collectOddsAndEvens([1, 2, 3, 4, 5, 6, 7, 8, 9]); // { odd: 5, even: 4 }
collectOddsAndEvens([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); // { odd: 5, even: 5 }
collectOddsAndEvens([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); // { odd: 6, even: 5 }

Hint: start with an object that already has odd: 0 and even: 0, then loop through the array and bump the right key by one each time, based on the remainder when dividing by 2.

Challenge 3: Count Arrays Containing a Value

Write a function countIfHasValue(collections, value) that accepts an object whose every value is an array, plus a number to search for. Return how many of those arrays contain that number.

Example:

javascript
const obj = { a: [1, 10, 3], b: [4, 1, 7], c: [7, 7, 7], d: [10, 7, 12] };

countIfHasValue(obj, 1);  // 2
countIfHasValue(obj, 7);  // 3
countIfHasValue(obj, 10); // 2
countIfHasValue(obj, 13); // 0

Hint: Object.values() gets you the arrays without needing their keys. From there, .includes() tells you whether a single array has the value, and .reduce() (or a simple loop with a counter) tallies how many did.

Challenge 4: Count Digit Characters in a String

Write a function countValidNumsInString(str) that accepts a string and returns how many of its characters are digits (0 through 9).

Example:

javascript
countValidNumsInString("");              // 0
countValidNumsInString("1");             // 1
countValidNumsInString("12");            // 2
countValidNumsInString("12abc3");        // 3
countValidNumsInString("1s2d3dsadas4");  // 4
countValidNumsInString("512,3,4!?!");    // 5
countValidNumsInString("123456");        // 6

Hint: this counts digit characters, not number tokens. In "512,3,4!?!" there are only three separate numbers (512, 3, 4), but five digit characters, so check each character on its own rather than splitting the string apart first. A regular expression like /[0-9]/ tests a single character, or you can compare against "0" and "9" directly.

Challenge 5: Divide an Object into Numbers and String Length

Write a function divideObject(data) that accepts an object whose values are a mix of strings and numbers. Return an array of two one-item arrays: the first holds a single number, the sum of every numeric value in the object; the second holds a single number, the combined character count of every string value in the object.

Example:

javascript
divideObject({
  first: "hi",
  second: "hello",
  third: "hey",
  fourth: 2,
  fifth: "fun",
  sixth: 10
}); // [ [12], [13] ]

Hint: loop through Object.values() once, and use typeof to decide whether each value feeds the running number total or the running character-count total. A string’s .length gives you its character count.

Challenge 6: Find First and Last Index

Write a function findFirstAndLastIndex(arr, num) that returns a two-item array: the index where num first appears in arr, then the index where it last appears. If num doesn’t appear at least twice (including if it’s missing entirely), return -1 on its own, not inside an array.

Example:

javascript
findFirstAndLastIndex([1, 2, 3, 4, 5], 3);    // -1
findFirstAndLastIndex([1, 2, 2, 2, 5], 12);   // -1
findFirstAndLastIndex([1, 2, 2, 2, 5], 2);    // [1, 3]

Hint: .indexOf() finds the first occurrence in one call. For the last occurrence, scan the array yourself and keep overwriting a variable every time you see a match, whatever value it holds once the loop ends is the last one. Then compare the first and last index you found, if they’re the same, the value only appeared once.

Challenge 7: Find the First Occurrence in a Grid

Write a function findFirstMove(moves, move) that accepts an array of arrays (a grid of moves) and a target move. Scanning row by row, left to right within each row, return a two-item array [row, column] for the first position where that move appears. If it never appears, return -1.

Example:

javascript
const moves = [
  ["a", "b", "c"],
  ["d", "a", "f"],
  ["g", "h", "h"]
];

findFirstMove(moves, "a"); // [0, 0]
findFirstMove(moves, "h"); // [2, 1]
findFirstMove(moves, "z"); // -1

Hint: loop over the outer array to get each row and its row number, and use .indexOf() on each row to check for the move and get its column in one step. As soon as a row gives you something other than -1, you have your answer.

Challenge 8: Find the Highest Priority Todo

Write a function findHighestPriorityTodo(todos) that accepts an array of objects, each with a task property and a priority property. Return a two-item array holding the task name with the highest priority, followed by that priority number.

Example:

javascript
findHighestPriorityTodo([
  { task: "Eat", priority: 18 },
  { task: "Sleep", priority: 22 },
  { task: "Solve problems", priority: 17 }
]); // ["Sleep", 22]

Hint: .reduce() without a starting value uses the first array item as the initial accumulator, which works well here since every candidate is already the same shape of object. Compare priority on each step and keep whichever object wins, then pull task and priority off the winner at the end.

Challenge 9: Check if a Value is in a Matrix

Write a function inMatrix(matrix, value) that accepts an array of arrays (a matrix) and a value. Return true if that value appears anywhere in the matrix, false otherwise.

Example:

javascript
const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

inMatrix(matrix, 5);  // true
inMatrix(matrix, 8);  // true
inMatrix(matrix, 10); // false

Hint: you don’t need to flatten anything. Loop over the rows and ask each row, with .includes(), whether it contains the value, if any row says yes, you’re done.

Challenge 10: Replace Two Elements at an Index

Write a function replaceAfter(arr, index) that removes two elements starting at index and puts the strings "Hello" and "world" in their place. Return the result as a new array, don’t modify the original.

Example:

javascript
replaceAfter(["1", "2", "a", "b", "3", "4"], 2); // ["1", "2", "Hello", "world", "3", "4"]
replaceAfter(["a", "b", "c"], 0);                // ["Hello", "world", "c"]

Constraint: don’t reach for .splice(), it isn’t covered in this course. Build the new array using .slice() and the spread operator instead.

Hint: you need three pieces: everything before index, the two replacement strings, and everything after the two removed elements. .slice(0, index) gets the first piece, and .slice(index + 2) gets the last, spread them around "Hello" and "world" in a new array literal.

Challenge 11: Reverse Values with a Skip Rule

Write a function reverseValues(nums) that accepts an array of numbers. Scan through it: whenever you hit an even number, skip it and also skip the two numbers after it. Every number that isn’t skipped should end up in a new array, in the reverse of the order it appeared in the original. Return that new array.

Example:

javascript
reverseValues([1, 1, 3, 3, 2]);          // [3, 3, 1, 1]
reverseValues([1, 3, 5, 7]);             // [7, 5, 3, 1]
reverseValues([1, 3, 4, 7]);             // [3, 1]
reverseValues([11, 13, 15, 20, 1, 1]);   // [15, 13, 11]
reverseValues([4, 5, 1, 1, 2, 1, 1]);    // [1]
reverseValues([2, 2, 2]);                // []

Constraint: .reverse() isn’t covered in this course, and it wouldn’t fit here anyway. The skip logic (“skip the next two”) only makes sense scanned forward, so you can’t scan backward and expect the same result.

Hint: since you have to scan forward but the output needs to come out backward, don’t collect kept items onto the end of your result array. Instead, add each new kept item to the front of the result as you go, something like result = [item, ...result]. By the time the loop finishes, the array has naturally ended up in reverse order without ever calling .reverse().

Challenge 12: Count Robot Instructions

Write a function robotInstructions(moves) that accepts an array of moves, each one "U", "D", "L", or "R". Return an object with one key per move that appeared, where the value is how many times it appeared.

Example:

javascript
robotInstructions(["U", "D", "L", "R"]);
// { "U": 1, "D": 1, "L": 1, "R": 1 }

robotInstructions(["U", "D", "L", "R", "U", "D", "L", "R", "U", "D", "L", "R"]);
// { "U": 3, "D": 3, "L": 3, "R": 3 }

Hint: this is a tallying problem: start with an empty object, and for each move, either initialize its count to 1 or add 1 to whatever count is already there. move in counts or counts[move] || 0 are both ways to check whether you’ve seen it before.

Challenge 13: Compare Two Weekly Schedules

Write a function scheduleCheck(scheduleA, scheduleB) that accepts two objects, each with a key for every day of the week and a true/false value. Return how many days both objects have a value of true.

Example:

javascript
const weekA = { Monday: true, Tuesday: true, Wednesday: true, Thursday: true, Friday: true, Saturday: true, Sunday: true };
const weekB = { Monday: true, Tuesday: true, Wednesday: true, Thursday: true, Friday: true, Saturday: true, Sunday: true };

scheduleCheck(weekA, weekB); // 7

const weekC = { Monday: true, Tuesday: true, Wednesday: true, Thursday: true, Friday: true, Saturday: false, Sunday: true };

scheduleCheck(weekA, weekC); // 6

Hint: Object.keys() on either object gives you the seven day names to check. For each day, look up that same key on both objects and only count it if both are true.

Challenge 14: Separate Languages

Write a function separateLanguages(items) that accepts an array of strings and returns an object with three keys: "python" and "javascript", whose values are counts of how many times those exact strings appear, and "other", whose value is an array holding every other string, in the order they appeared.

Example:

javascript
separateLanguages(["python", "python", "python", "javascript", "c++"]);
// { python: 3, javascript: 1, other: ["c++"] }

separateLanguages(["python", "python", "spanish", "javascript"]);
// { python: 2, javascript: 1, other: ["spanish"] }

separateLanguages(["greek", "french", "yoruba", "python"]);
// { python: 1, javascript: 0, other: ["greek", "french", "yoruba"] }

Hint: start with { python: 0, javascript: 0, other: [] } so every key exists even if a string never shows up. Loop through the array once and route each string to the right place with a simple if/else if/else.

Challenge 15: Skip Vowels

Write a function skipVowels(str) that accepts a string and returns an array. Scan through the string one character at a time: if the current character is a vowel, skip it and also skip the character right after it; otherwise, add the current character to the result array.

Example:

javascript
skipVowels("hello");    // ["h", "l"]
skipVowels("much fun"); // ["m", "h", " ", "f"]
skipVowels("aaaa");     // []

Hint: a for...of loop won’t let you jump the index forward on demand, since it advances one character at a time no matter what. Use a while loop with your own index variable instead, so you can add 2 to it when you hit a vowel and 1 otherwise.

Recap

Fifteen challenges, and every one of them leaned on a data shape more complex than a flat array or a single object: arrays of objects, objects full of arrays, grids, and objects being compared key by key. Nothing here needed a technique you haven’t already seen, only the willingness to combine Object.keys()/Object.values(), .reduce(), and careful loop indexing in ways the earlier lessons didn’t demand.

That’s every challenge in this course, and the last lesson of JavaScript Fundamentals. The next course picks up right where this one leaves off: the DOM, browser events, and talking to servers.