DEV Community

James Robb
James Robb

Posted on • Edited on

2

Take a Ten Minute Walk

Task description

You live in the city of Cartesia where all roads are laid out in a perfect grid. You arrived ten minutes too early to an appointment, so you decided to take the opportunity to go for a short walk. The city provides its citizens with a Walk Generating App on their phones -- everytime you press the button it sends you an array of one-letter strings representing directions to walk (eg. ['n', 's', 'w', 'e']). You always walk only a single block in a direction and you know it takes you one minute to traverse one city block, so create a function that will return true if the walk the app gives you will take you exactly ten minutes (you don't want to be early or late!) and will, of course, return you to your starting point. Return false otherwise.

Note: you will always receive a valid array containing a random assortment of direction letters ('n', 's', 'e', or 'w' only). It will never give you an empty array (that's not a walk, that's standing still!).

Task solution

Tests

describe("walk validator", () => {
  it("Throws when invalid input is provided", () => {
    expect(() => isValidWalk("w")).toThrow(/InvalidArgumentException/);
    expect(() => isValidWalk(["w", 2])).toThrow(/InvalidArgumentException/);
    expect(() => isValidWalk(["w", "test"])).toThrow(/InvalidArgumentException/);
    expect(() => isValidWalk(["w"], ["2", 2])).toThrow(/InvalidArgumentException/);
    expect(() => isValidWalk(["w"], 1)).toThrow(/InvalidArgumentException/);
    expect(() => isValidWalk(["w"], [1, 1, 1])).toThrow(/InvalidArgumentException/);
    expect(() => isValidWalk(["w"], [0, 0], "ten")).toThrow(/InvalidArgumentException/);
  });

  it("Should correctly identify walkable directions", () => {
    expect(isValidWalk(["n", "s", "n", "s", "n", "s", "n", "s", "n", "s"])).toBe(true);
    expect(isValidWalk(["w", "e", "w"])).toBe(false);
    expect(isValidWalk(["w"])).toBe(false);
    expect(isValidWalk(["w", "e"], [1, 1], 2)).toBe(true);
  });
});
Enter fullscreen mode Exit fullscreen mode

Using Jest for our tests, we begin by defining our failing input cases as usual. In our case these are:

  1. Are the directions not an array?
  2. Are the instructions all strings?
  3. Are the instructions expected strings ("n", "s", "e" or "w")?
  4. Are the starting points (if defined) integers?
  5. Are the starting points matching the expected [x1, y1] shape?
  6. Are we able to use this function for any amount of time depending on user case?

Then we test the happy paths to be sure that our function can correctly identify valid pathways which bring us back to our starting point after the final direction is executed.

Implementation

function isValidWalk(walk, startingPosition = [0, 0], timeAvailableMinutes = 10) {
  if (!Array.isArray(walk)) {
    throw new Error(`InvalidArgumentException: Parameter 1 must be an array. Received: ${typeof walk}`);
  } else if (!walk.every(item => typeof item === "string")) {
    throw new Error("InvalidArgumentException: Parameter 1 must be an array of strings, atleast one element within the array provided is not a string");
  } else if(!walk.every(item => ["n", "s", "e", "w"].includes(item))) {
    throw new Error("InvalidArgumentException: Parameter 1 must be an array of strings. Each string must correspond to a compass direction, valid directions are: 'n', 's', 'e' and 'w'");
  } else if (!Array.isArray(startingPosition)) {
    throw new Error(`InvalidArgumentException: Parameter 2 must be an array. Received: ${typeof startingPosition}`);
  } else if(startingPosition.length !== 2) {
    throw new Error(`InvalidArgumentException: Parameter 2 must have 2 items representing the starting position of the user. Received: ${startingPosition} with a length of ${startingPosition.length}`);
  } else if(!startingPosition.every(item => Number.isInteger(item))) {
    throw new Error(`InvalidArgumentException: Parameter 2 must be an array of numbers and have a length of 2 items. This is to match the schema requirement of [x1: number, y1: number]. Received: ${startingPosition}`);
  } else if(!Number.isInteger(timeAvailableMinutes)) {
    throw new Error(`InvalidArgumentException: Parameter 3 must be an integer. Received: ${typeof timeAvailableMinutes}`);
  }

  const [x1, y1] = startingPosition;
  const [x2, y2] = walk.reduce(([x, y], direction) => {
    switch (direction) {
      case 'n': return [x, y + 1];
      case 's': return [x, y - 1];
      case 'e': return [x + 1, y];
      case 'w': return [x - 1, y];
    }
  }, [x1, y1]);
  return walk.length === timeAvailableMinutes && x1 === x2 && y1 === y2;
}
Enter fullscreen mode Exit fullscreen mode

We run our input checks and then begin to reason our coordinates. Firstly we strip the starting x and y positions of the user and name these x1 and y1.

Next, we take the walk array of directions and reduce it to an array of x2 and y2 positions. To achieve this, the initial "accumulator" of the reducer is set to x1 and y1 and on each iteration of the reducer, based on the current direction, we either increment or decrement x or y. Upon the reducers final iteration, these values will now be our x2 and y2 coordinates.

Finally we check if the walk had the same amount of items as the minutes it takes per direction (as outlined in the task description) and from there we check if the start and end x and y values match. If all of these criteria match, we know the walk is valid since the walk time matches the available time and the end positions match the starting ones.

Conclusions

This challenge was a good use case for reducers and the only change I would probably make to the implementation is to return early if the walk and time available don't match, like so:

// code removed for reading ease
if(walk.length !== timeAvailableMinutes) return false;
const [x1, y1] = startingPosition;
const [x2, y2] = walk.reduce(([x, y], direction) => {
  switch (direction) {
    case 'n': return [x, y + 1];
    case 's': return [x, y - 1];
    case 'e': return [x + 1, y];
    case 'w': return [x - 1, y];
  }
}, [x1, y1]);
return x1 === x2 && y1 === y2;
// code removed for reading ease
Enter fullscreen mode Exit fullscreen mode

Hot sauce if you're wrong - web dev trivia for staff engineers

Hot sauce if you're wrong · web dev trivia for staff engineers (Chris vs Jeremy, Leet Heat S1.E4)

  • Shipping Fast: Test your knowledge of deployment strategies and techniques
  • Authentication: Prove you know your OAuth from your JWT
  • CSS: Demonstrate your styling expertise under pressure
  • Acronyms: Decode the alphabet soup of web development
  • Accessibility: Show your commitment to building for everyone

Contestants must answer rapid-fire questions across the full stack of modern web development. Get it right, earn points. Get it wrong? The spice level goes up!

Watch Video 🌶️🔥

Top comments (0)

Image of PulumiUP 2025

Let's talk about the current state of cloud and IaC, platform engineering, and security.

Dive into the stories and experiences of innovators and experts, from Startup Founders to Industry Leaders at PulumiUP 2025.

Register Now

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay