Refactored the code for better readability, consistency and modularity:
- Changed the behaviour of
findTheBomb()
to throw error when called with an empty array, as in the original request. (Returning a string as error message made it hard to asses the result of the function programmatically.) - Changed the behaviour of
findTheBomb()
to return empty array when no bomb is found. This is to make the return value more predictable. - Introduced the
box()
andwithBomb()
functions to make the test cases (and debugging) easier to construct and more readable. - Abstracted the main function into more generic functions to help readability, reusability and testing.
- Simplified the names for better readability.
export interface Box { code: string bomb?: boolean boxes?: Box[] } /** * Creates a box with optional boxes inside */ export function box(code: string, boxes?: Box[]): Box { return boxes ? { code, boxes } : { code } } /** * Returns a copy of a box with bomb */ export function withBomb(box: Box) { return { ...box, bomb: true } } /** * Finds the bomb in a box */ export function pathToBomb(box: Box, path: string[] = []): string[] { const newPath = [...path, box.code] if (box.bomb) return newPath if (!Array.isArray(box.boxes)) return [] return traverseBoxes(box.boxes, newPath) } /** * Finds the bomb in an array of boxes */ export function traverseBoxes(boxes: Box[], path: string[] = []): string[] { return boxes.reduce((result: string[], box) => ( result.length > 0 ? result : pathToBomb(box, path) ), []) } /** * The main function */ export function findTheBomb(boxes: Box[]): string { if (boxes.length === 0) throw new Error('Empty array') return traverseBoxes(boxes).join(' > ') }
interface ListOfBoxes {- export interface Box {
- code: string
- bomb?: boolean
boxes?: ListOfBoxes[]- boxes?: Box[]
- }
export function findTheBomb (listOfBoxes: ListOfBoxes [], path: string[] = []): string | null {if (!listOfBoxes.length) {return "No Boxes No Bomb!"}for (const box of listOfBoxes) {const newPath = [...path, box.code]if (box.bomb) {return newPath.join(" > ")}if (box.boxes) {const found = findTheBomb(box.boxes, newPath)if (found) {return found}}}- /**
- * Creates a box with optional boxes inside
- */
- export function box(code: string, boxes?: Box[]): Box {
- return boxes ? { code, boxes } : { code }
- }
- /**
- * Returns a copy of a box with bomb
- */
- export function withBomb(box: Box) {
- return { ...box, bomb: true }
- }
- /**
- * Finds the bomb in a box
- */
- export function pathToBomb(box: Box, path: string[] = []): string[] {
- const newPath = [...path, box.code]
return null- if (box.bomb) return newPath
- if (!Array.isArray(box.boxes)) return []
- return traverseBoxes(box.boxes, newPath)
- }
const listBoxes1: ListOfBoxes [] = [{code: "B1",boxes: [{code: "B1.1"},{code: "B1.2"}]},{code: "B2",boxes: [{code: "B2.1",boxes: [{code: "B2.1.1"},{code: "B2.1.2",bomb: true}]},{code: "B2.2"}]}]const listBoxes2: ListOfBoxes [] = [{"code": "B1.2","boxes": [{"code": "B1.2.2","boxes": [{"code": "B1.2.2.6"}]},{"code": "B1.2.7"},{"code": "B1.2.4"},{"code": "B1.2.0"},{"code": "B1.2.9","boxes": [{"code": "B1.2.9.0","bomb": true,}]}]},{"code": "B2.8","boxes": [{"code": "B2.8.0"},{"code": "B2.8.7"},{"code": "B2.8.6"},{"code": "B2.8.3"}]},{"code": "B3.6","boxes": [{"code": "B3.6.5"},{"code": "B3.6.7","boxes": [{"code": "B3.6.7.9"}]}]}]const listBoxes3: ListOfBoxes [] = [{"code": "B1.6","boxes": [{"code": "B1.6.9"},{"code": "B1.6.2"}]},{"code": "B2.4","boxes": [{"code": "B2.4.0"},{"code": "B2.4.3","boxes": [{"code": "B2.4.3.9","bomb": true}]},{"code": "B2.4.7"}]}]findTheBomb([])findTheBomb(listBoxes1);findTheBomb(listBoxes2);findTheBomb(listBoxes3);- /**
- * Finds the bomb in an array of boxes
- */
- export function traverseBoxes(boxes: Box[], path: string[] = []): string[] {
- return boxes.reduce((result: string[], box) => (
- result.length > 0 ? result : pathToBomb(box, path)
- ), [])
- }
- /**
- * The main function
- */
- export function findTheBomb(boxes: Box[]): string {
- if (boxes.length === 0) throw new Error('Empty array')
- return traverseBoxes(boxes).join(' > ')
- }
// Since Node 10, we're using Mocha. // You can use `chai` for assertions. const chai = require("chai"); const { assert, expect } = chai; // Uncomment the following line to disable truncating failure messages for deep equals, do: // chai.config.truncateThreshold = 0; // Since Node 12, we no longer include assertions from our deprecated custom test framework by default. // Uncomment the following to use the old assertions: // const Test = require("@codewars/test-compat"); import { Box, box, withBomb, findTheBomb } from "./solution"; const list0: Box[] = [ box('B1', [ box('B1.1'), box('B1.2') ]), box('B2', [ box('B2.1', [ box('B2.1.1'), box('B2.1.2'), ]), box('B2.2'), ]), ] const list1: Box[] = [ box('B1', [ box('B1.1'), box('B1.2') ]), box('B2', [ box('B2.1', [ box('B2.1.1'), withBomb(box('B2.1.2')) ]), box('B2.2'), ]), ] const list2: Box[] = [ box('B1.2', [ box('B1.2.2', [ box('B1.2.2.6') ]), box('B1.2.7'), box('B1.2.4'), box('B1.2.0'), box('B1.2.9', [ withBomb(box('B1.2.9.0')) ]), ]), box('B2.8', [ box('B2.8.0'), box('B2.8.7'), box('B2.8.6'), box('B2.8.3')]), box('B3.6', [ box('B3.6.5'), box('B3.6.7', [ box('B3.6.7.9') ]) ]), ] const list3: Box[] = [ box('B1.6', [ box('B1.6.9'), box('B1.6.2') ]), box('B2.4', [ box('B2.4.0'), box('B2.4.3', [ withBomb(box('B2.4.3.9')) ]), box('B2.4.7'), ]), ] const list4: Box[] = [ withBomb(box('B')) ] describe("findTheBomb()", () => { it("should throw error if `boxes` is an empty array", () => { expect(() => findTheBomb([])).to.throw(Error, "Empty array") }) it("should find the bomb in a list with a single box", () => { const result = findTheBomb(list4) assert.strictEqual(result, "B") }) it("should return an empty string when there is no bomb", () => { const result = findTheBomb(list0) assert.strictEqual(result, '') }) it("should find the bomb in list1", () => { const result = findTheBomb(list1) assert.strictEqual(result, "B2 > B2.1 > B2.1.2") }) it("should find the bomb in list2", () => { const result = findTheBomb(list2) assert.strictEqual(result, "B1.2 > B1.2.9 > B1.2.9.0") }) it("should find the bomb in list3", () => { const result = findTheBomb(list3) assert.strictEqual(result, "B2.4 > B2.4.3 > B2.4.3.9") }) });
- // Since Node 10, we're using Mocha.
- // You can use `chai` for assertions.
- const chai = require("chai");
const assert = chai.assert;- const { assert, expect } = chai;
- // Uncomment the following line to disable truncating failure messages for deep equals, do:
- // chai.config.truncateThreshold = 0;
- // Since Node 12, we no longer include assertions from our deprecated custom test framework by default.
- // Uncomment the following to use the old assertions:
- // const Test = require("@codewars/test-compat");
import { findTheBomb } from "./solution";- import { Box, box, withBomb, findTheBomb } from "./solution";
interface ListOfBoxes {code: string,bomb?: boolean,boxes?: ListOfBoxes[]}const listBoxes1: ListOfBoxes[] = [{code: "B1",boxes: [{code: "B1.1"},{code: "B1.2"}]},{code: "B2",boxes: [{code: "B2.1",boxes: [{code: "B2.1.1"},{code: "B2.1.2",bomb: true}]},{code: "B2.2"}]}- const list0: Box[] = [
- box('B1', [
- box('B1.1'),
- box('B1.2')
- ]),
- box('B2', [
- box('B2.1', [
- box('B2.1.1'),
- box('B2.1.2'),
- ]),
- box('B2.2'),
- ]),
- ]
const listBoxes2: ListOfBoxes[] = [{"code": "B1.2","boxes": [{"code": "B1.2.2","boxes": [{"code": "B1.2.2.6"}]},{"code": "B1.2.7"},{"code": "B1.2.4"},{"code": "B1.2.0"},{"code": "B1.2.9","boxes": [{"code": "B1.2.9.0","bomb": true,}]}]},{"code": "B2.8","boxes": [{"code": "B2.8.0"},{"code": "B2.8.7"},{"code": "B2.8.6"},{"code": "B2.8.3"}]},{"code": "B3.6","boxes": [{"code": "B3.6.5"},{"code": "B3.6.7","boxes": [{"code": "B3.6.7.9"}]}]}- const list1: Box[] = [
- box('B1', [
- box('B1.1'),
- box('B1.2')
- ]),
- box('B2', [
- box('B2.1', [
- box('B2.1.1'),
- withBomb(box('B2.1.2'))
- ]),
- box('B2.2'),
- ]),
- ]
- const list2: Box[] = [
- box('B1.2', [
- box('B1.2.2', [
- box('B1.2.2.6')
- ]),
- box('B1.2.7'),
- box('B1.2.4'),
- box('B1.2.0'),
- box('B1.2.9', [
- withBomb(box('B1.2.9.0'))
- ]),
- ]),
- box('B2.8', [
- box('B2.8.0'),
- box('B2.8.7'),
- box('B2.8.6'),
- box('B2.8.3')]),
- box('B3.6', [
- box('B3.6.5'),
- box('B3.6.7', [
- box('B3.6.7.9')
- ])
- ]),
- ]
const listBoxes3: ListOfBoxes[] = [{"code": "B1.6","boxes": [{"code": "B1.6.9"},{"code": "B1.6.2"}]},{"code": "B2.4","boxes": [{"code": "B2.4.0"},{"code": "B2.4.3","boxes": [{"code": "B2.4.3.9","bomb": true}]},{"code": "B2.4.7"}]}- const list3: Box[] = [
- box('B1.6', [
- box('B1.6.9'),
- box('B1.6.2')
- ]),
- box('B2.4', [
- box('B2.4.0'),
- box('B2.4.3', [
- withBomb(box('B2.4.3.9'))
- ]),
- box('B2.4.7'),
- ]),
- ]
- const list4: Box[] = [
- withBomb(box('B'))
- ]
describe("Find The Bomb", function() {it("should return `No Boxes No Bomb!` if give empty arrays as arg", function () {const result = findTheBomb([])assert.strictEqual(result, "No Boxes No Bomb!")- describe("findTheBomb()", () => {
- it("should throw error if `boxes` is an empty array", () => {
- expect(() => findTheBomb([])).to.throw(Error, "Empty array")
- })
- it("should find the bomb in a list with a single box", () => {
- const result = findTheBomb(list4)
- assert.strictEqual(result, "B")
- })
it("should return with format `([BOX_CODE] >?)+` listOfBoxes1", function () {const result = findTheBomb(listBoxes1)- it("should return an empty string when there is no bomb", () => {
- const result = findTheBomb(list0)
- assert.strictEqual(result, '')
- })
- it("should find the bomb in list1", () => {
- const result = findTheBomb(list1)
- assert.strictEqual(result, "B2 > B2.1 > B2.1.2")
- })
it("should return with format `([BOX_CODE] >?)+` listOfBoxes2", function () {const result = findTheBomb(listBoxes2)- it("should find the bomb in list2", () => {
- const result = findTheBomb(list2)
- assert.strictEqual(result, "B1.2 > B1.2.9 > B1.2.9.0")
- })
it("should return with format `([BOX_CODE] >?)+` listOfBoxes3", function () {const result = findTheBomb(listBoxes3)- it("should find the bomb in list3", () => {
- const result = findTheBomb(list3)
- assert.strictEqual(result, "B2.4 > B2.4.3 > B2.4.3.9")
- })
- });