Created
October 13, 2021 22:44
-
-
Save KOMON/444c404306918abab77a77f5054069e0 to your computer and use it in GitHub Desktop.
Remitly Take Home assignment
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
/* eslint-disable */ | |
/* | |
/* | |
You are implementing an elevator. | |
The elevator has 4 actions: | |
UP_ONE | |
DOWN_ONE | |
OPEN_CLOSE | |
NO_OP | |
The elevator class has 2 functions: | |
def requestFloor(n): | |
// Called when the elevator is summoned to floor n, once per button press. Returns nothing. | |
def getAction(): | |
// Returns next action to do. See "additional guidance" items 3 & 4. | |
- Implement requestFloor and getAction | |
- What is the complexity of requestFloor and getAction? | |
- How would you measure success? | |
*/ | |
var __assign = (this && this.__assign) || function () { | |
__assign = Object.assign || function(t) { | |
for (var s, i = 1, n = arguments.length; i < n; i++) { | |
s = arguments[i]; | |
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | |
t[p] = s[p]; | |
} | |
return t; | |
}; | |
return __assign.apply(this, arguments); | |
}; | |
exports.__esModule = true; | |
exports.expectActions = exports.test = exports.Elevator = void 0; | |
var assert = require("assert"); | |
var Elevator = /** @class */ (function () { | |
function Elevator() { | |
// User requests floor: | |
// Keep track of that floor | |
// Keep track of what floor we're on | |
// getAction will need to emit directionals until we get to the correct floor | |
// get action will need to OPEN_CLOSE when we're at a requested floor | |
// getAction will NO_OP if no requests exists | |
this.currentFloor = 0; | |
this.currentDirection = 'UP'; | |
this.floorRequests = new Map(); | |
} | |
Elevator.prototype.requestFloor = function (n) { | |
if (n === this.currentFloor) { | |
return; | |
} | |
// if floors is empty, assume we're not travelling in a particular direction | |
if (this.floorRequests.size === 0) { | |
if (n < this.currentFloor) { | |
this.currentDirection = 'DOWN'; | |
} | |
else { | |
this.currentDirection = 'UP'; | |
} | |
} | |
this.floorRequests.set(n, true); | |
}; | |
Elevator.prototype.searchFloorsForClosest = function () { | |
// search for the current floor if we can | |
var _this = this; | |
// return the current floor if found | |
if (this.floorRequests.get(this.currentFloor)) { | |
return this.currentFloor; | |
} | |
// if we can't find the current floor | |
// iterate over all the entries in the map, collecting results above and below currentFloor | |
// trying to find z or a below | |
// [ ... x, y, z, <currentFloor?>, a, b, c, ...] | |
var _a = Array.from(this.floorRequests.keys()).reduce(function (carry, current) { | |
if ( | |
// we're on the xyz side | |
current < _this.currentFloor && | |
// and this is a better candidate for z | |
(!carry.nextLower || current > carry.nextLower)) { | |
return __assign(__assign({}, carry), { nextLower: current }); | |
} | |
else if ( | |
// we're on the abc side | |
current > _this.currentFloor && | |
// and this is a better candidate for a | |
(!carry.nextHigher || current < carry.nextHigher)) { | |
return __assign(__assign({}, carry), { nextHigher: current }); | |
} | |
return carry; | |
}, { nextHigher: null, nextLower: null }), nextHigher = _a.nextHigher, nextLower = _a.nextLower; | |
if (this.currentDirection === 'UP') { | |
return nextHigher !== null && nextHigher !== void 0 ? nextHigher : nextLower; | |
} | |
else { | |
return nextLower !== null && nextLower !== void 0 ? nextLower : nextHigher; | |
} | |
}; | |
Elevator.prototype.getAction = function () { | |
// if floors is empty, no_op | |
if (this.floorRequests.size === 0) { | |
return 'NO_OP'; | |
} | |
// search the array, looking for either floor we're on, or the next greatest floor | |
var foundFloor = this.searchFloorsForClosest(); | |
if (foundFloor === this.currentFloor) { | |
this.floorRequests["delete"](this.currentFloor); | |
return 'OPEN_CLOSE'; | |
} | |
else if (foundFloor > this.currentFloor) { | |
this.currentDirection = 'UP'; | |
this.currentFloor++; | |
return 'UP_ONE'; | |
} | |
else if (foundFloor < this.currentFloor) { | |
this.currentDirection = 'DOWN'; | |
this.currentFloor--; | |
return 'DOWN_ONE'; | |
} | |
}; | |
return Elevator; | |
}()); | |
exports.Elevator = Elevator; | |
function test() { | |
try { | |
var elevator = new Elevator(); | |
elevator.requestFloor(4); // go to the 4th floor | |
expectActions(elevator, [ | |
{ action: 'UP_ONE', count: 4 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], 'Go to the 4th floor'); | |
elevator.requestFloor(3); | |
elevator.requestFloor(30); | |
elevator.requestFloor(17); | |
elevator.requestFloor(5); | |
elevator.requestFloor(8); | |
expectActions(elevator, [ | |
{ action: 'DOWN_ONE', count: 1 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], 'Go to the 3rd floor (first wins)'); | |
expectActions(elevator, [ | |
{ action: 'UP_ONE', count: 2 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], 'Go to the 5th floor'); | |
elevator.requestFloor(2); | |
expectActions(elevator, [ | |
{ action: 'UP_ONE', count: 3 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], 'Go to the 8th floor'); | |
expectActions(elevator, [ | |
{ action: 'UP_ONE', count: 9 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], 'Go to the 17th floor'); | |
expectActions(elevator, [ | |
{ action: 'UP_ONE', count: 13 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], 'Go to the 30th floor'); | |
expectActions(elevator, [ | |
{ action: 'DOWN_ONE', count: 28 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], 'Go to the second floor (requested on the 5th floor earlier)'); | |
} | |
catch (err) { | |
console.error("Test failed! -- " + err.message); | |
return; | |
} | |
console.log('Test succeeded!'); | |
} | |
exports.test = test; | |
function expectActions(elevator, expectations, descriptor) { | |
expectations.forEach(function (_a) { | |
var action = _a.action, count = _a.count; | |
var current = 1; | |
var currentAction; | |
while (current < count + 1 && currentAction !== 'NO_OP') { | |
currentAction = elevator.getAction(); | |
assert.strictEqual(action, currentAction, "" + (descriptor ? descriptor : '') + (descriptor ? ': ' : '') + "Expected " + current + "th " + action + " but got " + currentAction + " instead"); | |
current++; | |
} | |
}); | |
} | |
exports.expectActions = expectActions; | |
if (require.main === module) { | |
test(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable */ | |
/* | |
/* | |
You are implementing an elevator. | |
The elevator has 4 actions: | |
UP_ONE | |
DOWN_ONE | |
OPEN_CLOSE | |
NO_OP | |
The elevator class has 2 functions: | |
def requestFloor(n): | |
// Called when the elevator is summoned to floor n, once per button press. Returns nothing. | |
def getAction(): | |
// Returns next action to do. See "additional guidance" items 3 & 4. | |
- Implement requestFloor and getAction | |
- What is the complexity of requestFloor and getAction? | |
- How would you measure success? | |
*/ | |
import * as assert from 'assert'; | |
type Action = 'UP_ONE' | 'DOWN_ONE' | 'OPEN_CLOSE' | 'NO_OP'; | |
type Direction = 'UP' | 'DOWN'; | |
export class Elevator { | |
// User requests floor: | |
// Keep track of that floor | |
// Keep track of what floor we're on | |
// getAction will need to emit directionals until we get to the correct floor | |
// get action will need to OPEN_CLOSE when we're at a requested floor | |
// getAction will NO_OP if no requests exists | |
private currentFloor = 0; | |
private currentDirection: Direction = 'UP'; | |
private floorRequests: Map<number, boolean> = new Map(); | |
public requestFloor(n: number) { | |
if (n === this.currentFloor) { | |
return; | |
} | |
// if floors is empty, assume we're not travelling in a particular direction | |
if (this.floorRequests.size === 0) { | |
if (n < this.currentFloor) { | |
this.currentDirection = 'DOWN'; | |
} else { | |
this.currentDirection = 'UP'; | |
} | |
} | |
this.floorRequests.set(n, true); | |
} | |
private searchFloorsForClosest(): number { | |
// search for the current floor if we can | |
// return the current floor if found | |
if (this.floorRequests.get(this.currentFloor)) { | |
return this.currentFloor; | |
} | |
// if we can't find the current floor | |
// iterate over all the entries in the map, collecting results above and below currentFloor | |
// trying to find z or a below | |
// [ ... x, y, z, <currentFloor?>, a, b, c, ...] | |
const { nextHigher, nextLower } = Array.from( | |
this.floorRequests.keys(), | |
).reduce( | |
(carry, current) => { | |
if ( | |
// we're on the xyz side | |
current < this.currentFloor && | |
// and this is a better candidate for z | |
(!carry.nextLower || current > carry.nextLower) | |
) { | |
return { ...carry, nextLower: current }; | |
} else if ( | |
// we're on the abc side | |
current > this.currentFloor && | |
// and this is a better candidate for a | |
(!carry.nextHigher || current < carry.nextHigher) | |
) { | |
return { ...carry, nextHigher: current }; | |
} | |
return carry; | |
}, | |
{ nextHigher: null, nextLower: null }, | |
); | |
if (this.currentDirection === 'UP') { | |
return nextHigher ?? nextLower; | |
} else { | |
return nextLower ?? nextHigher; | |
} | |
} | |
public getAction(): Action { | |
// if floors is empty, no_op | |
if (this.floorRequests.size === 0) { | |
return 'NO_OP'; | |
} | |
// search the array, looking for either floor we're on, or the next greatest floor | |
const foundFloor = this.searchFloorsForClosest(); | |
if (foundFloor === this.currentFloor) { | |
this.floorRequests.delete(this.currentFloor); | |
return 'OPEN_CLOSE'; | |
} else if (foundFloor > this.currentFloor) { | |
this.currentDirection = 'UP'; | |
this.currentFloor++; | |
return 'UP_ONE'; | |
} else if (foundFloor < this.currentFloor) { | |
this.currentDirection = 'DOWN'; | |
this.currentFloor--; | |
return 'DOWN_ONE'; | |
} | |
} | |
} | |
export function test() { | |
try { | |
const elevator = new Elevator(); | |
elevator.requestFloor(4); // go to the 4th floor | |
expectActions( | |
elevator, | |
[ | |
{ action: 'UP_ONE', count: 4 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], | |
'Go to the 4th floor', | |
); | |
elevator.requestFloor(3); | |
elevator.requestFloor(30); | |
elevator.requestFloor(17); | |
elevator.requestFloor(5); | |
elevator.requestFloor(8); | |
expectActions( | |
elevator, | |
[ | |
{ action: 'DOWN_ONE', count: 1 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], | |
'Go to the 3rd floor (first wins)', | |
); | |
expectActions( | |
elevator, | |
[ | |
{ action: 'UP_ONE', count: 2 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], | |
'Go to the 5th floor', | |
); | |
elevator.requestFloor(2); | |
expectActions( | |
elevator, | |
[ | |
{ action: 'UP_ONE', count: 3 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], | |
'Go to the 8th floor', | |
); | |
expectActions( | |
elevator, | |
[ | |
{ action: 'UP_ONE', count: 9 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], | |
'Go to the 17th floor', | |
); | |
expectActions( | |
elevator, | |
[ | |
{ action: 'UP_ONE', count: 13 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], | |
'Go to the 30th floor', | |
); | |
expectActions( | |
elevator, | |
[ | |
{ action: 'DOWN_ONE', count: 28 }, | |
{ action: 'OPEN_CLOSE', count: 1 }, | |
], | |
'Go to the second floor (requested on the 5th floor earlier)', | |
); | |
} catch (err) { | |
console.error(`Test failed! -- ${err.message}`); | |
return; | |
} | |
console.log('Test succeeded!'); | |
} | |
export function expectActions( | |
elevator: Elevator, | |
expectations: { action: Action; count: number }[], | |
descriptor?: string, | |
) { | |
expectations.forEach(({ action, count }) => { | |
let current = 1; | |
let currentAction: Action; | |
while (current < count + 1 && currentAction !== 'NO_OP') { | |
currentAction = elevator.getAction(); | |
assert.strictEqual( | |
action, | |
currentAction, | |
`${descriptor ? descriptor : ''}${ | |
descriptor ? ': ' : '' | |
}Expected ${current}th ${action} but got ${currentAction} instead`, | |
); | |
current++; | |
} | |
}); | |
} | |
if (require.main === module) { | |
test(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment