Take a Ten Minute Walk: A City Tour Solution

I recently came across the following problem on Codewars:

Take a Ten Minute Walk

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 -- every time you press the button it sends you an array of one-letter strings representing directions to walk (eg. ['n', 's', 'w', 'e']). 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.

I solved the problem with the following function:

function isValidWalk(walk) {
var directions = {
'n': 0,
's': 0,
'e': 0,
'w': 0
}
walk.forEach( function(direction) {
directions[direction]++;
}
var displacement = {
x: directions['n'] - directions['s'],
y: directions['e'] - directions['w']
}
/*it take 1 min to traverse 1 block & I only have 10 minutes,
& I also want to attend appointment, so i want to come from where i start.*/
return walk.length === 10 && displacement.x === 0 && displacement.y === 0;
}


var directions = {
'n': 0,
's': 0,
'e': 0,
'w': 0
}


Quotes are optional on all valid property names.

walk.forEach( function(direction) {
directions[direction]++;
}


Syntax error here. Plus, making whitespaces consistent would make this code less of an eye sore. Consider using an IDE that will yell at you for inconsistent formatting.

var displacement = {
x: directions['n'] - directions['s'],
y: directions['e'] - directions['w']
}


Storing in an object is unnecessary. x and y can just be variables here.

An alternate approach is to represent the directions into 2 columns, vertical and horizontal. Then the directions on the same axis are represented 1 and -1. This way, knowing if you're back is a matter of checking if both axes sum up to zero. No subtraction required.

   V  H
N  1  0
S -1  0
E  0  1
E  0  1
W  0 -1
Result: [0, 1]
// Off by 1 to the east. Also, length is less than 10.


This is also a good functional programming exercise on how to think of functions as data transformations instead of machine commands. In the following code, each array method transforms the data from directions to a boolean in three discrete steps.

const directionVector = {
N: [1, 0],
S: [-1, 0],
E: [0, 1],
W: [0, -1]
}

const isValidWalk = walk => walk.length === 10 && walk
.map(direction => directionVector[direction])
.reduce((prev, next) => [prev[0] + next[0], prev[1] + next[1]], [0, 0])
.every(v => v === 0)

// length check goes first so code doesn't even try if it's not 10
// map converts direction into vectors: N -> [V, H]
// reduce sums all vectors: [V1, H1] + [V2, H2] + ... = [Vs, Hs]
// every checks if sum values are all zero: Vs === 0, Hs === 0

// Too short
console.log(isValidWalk(['N', 'S', 'E', 'W', 'W', 'E']))

// Too long
console.log(isValidWalk(['N', 'S', 'E', 'W', 'N', 'S', 'E', 'W', 'N', 'S', 'E']))

// We're back!
console.log(isValidWalk(['N', 'S', 'E', 'W', 'N', 'S', 'E', 'W', 'N', 'S']))

// We're lost
console.log(isValidWalk(['N', 'S', 'E', 'W', 'N', 'S', 'E', 'W', 'N', 'E']))

All in all I think, your solution is rather elegant. A tiny improvement could be:

function isValidWalk(walk) {
var directions = {
N: 0,
S: 0,
E: 0,
W: 0
};

walk.forEach(function (direction) {
directions[direction]++;
});

var isHome = () => {
return directions['N'] == directions['S'] && directions['E'] == directions['W'];
}
/*it take 1 min to traverse 1 block & I only have 10 minutes,
& I also want to attend appointment, so i want to come from where i start.*/
return walk.length === 10 && isHome();
}


The only caveat is (as others have pointed out) that you don't check the walk.length before you start walking.

That could be solved by using array.reduce in a way like this:

function isValidWalk(directions) {
var isHome = (offsets) => {
return offsets['N'] == offsets['S'] && offsets['E'] == offsets['W'];
}

var walk = (offsets, dir) => {
offsets[dir]++;
return offsets;
}

return directions.length === 10 && isHome(directions.reduce(walk, { N: 0, S: 0, E: 0, W: 0 }));
}