From db40c9459bb252ca655191c4fbbb4f60a47c1b57 Mon Sep 17 00:00:00 2001 From: Niels Boecker <11090412+nielsboecker@users.noreply.github.com> Date: Wed, 16 Jun 2021 22:35:19 +0100 Subject: [PATCH 1/5] Update bubble-sort.solution.test.js --- specs/bubble-sort/bubble-sort.solution.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/bubble-sort/bubble-sort.solution.test.js b/specs/bubble-sort/bubble-sort.solution.test.js index 445aa000..542626f9 100644 --- a/specs/bubble-sort/bubble-sort.solution.test.js +++ b/specs/bubble-sort/bubble-sort.solution.test.js @@ -14,7 +14,7 @@ function bubbleSort(nums) { let swapped = false; do { swapped = false; - for (let i = 0; i < nums.length; i++) { + for (let i = 0; i < nums.length - 1; i++) { // snapshot(nums); if (nums[i] > nums[i + 1]) { const temp = nums[i]; From 37eb14f94cab242b1831d980806c6d728ac72d11 Mon Sep 17 00:00:00 2001 From: Niels Boecker <11090412+nielsboecker@users.noreply.github.com> Date: Wed, 16 Jun 2021 22:37:07 +0100 Subject: [PATCH 2/5] Update radix-sort.solution.test.js --- specs/radix-sort/radix-sort.solution.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/radix-sort/radix-sort.solution.test.js b/specs/radix-sort/radix-sort.solution.test.js index f3bb6322..fe5b6b95 100644 --- a/specs/radix-sort/radix-sort.solution.test.js +++ b/specs/radix-sort/radix-sort.solution.test.js @@ -49,7 +49,7 @@ function radixSort(array) { // unit tests // do not modify the below code -describe.skip("radix sort", function () { +describe("radix sort", function () { it("should sort correctly", () => { const nums = [ 20, @@ -104,7 +104,7 @@ describe.skip("radix sort", function () { const nums = new Array(fill) .fill() .map(() => Math.floor(Math.random() * 500000)); - const ans = radixSort(nums); - expect(ans).toEqual(nums.sort()); + const ans = radixSort([...nums]); + expect(ans).toEqual(nums.sort((a, b) => a - b)); }); }); From 7b15b8ccf23cbc447f45306b1151a7be09f6e401 Mon Sep 17 00:00:00 2001 From: Niels Boecker <11090412+nielsboecker@users.noreply.github.com> Date: Sun, 27 Jun 2021 10:43:02 +0100 Subject: [PATCH 3/5] bloom --- specs/bloom-filters/bloom-filters.test.js | 28 +++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/specs/bloom-filters/bloom-filters.test.js b/specs/bloom-filters/bloom-filters.test.js index d70651cb..a0172cbd 100644 --- a/specs/bloom-filters/bloom-filters.test.js +++ b/specs/bloom-filters/bloom-filters.test.js @@ -2,46 +2,60 @@ // a library called xxhashjs is being loaded (as XXH) and we're using three different // instances of that as your hashing functions const XXH = require("xxhashjs"); + const h1 = (string) => Math.abs(XXH.h32(0xabcd).update(string).digest().toNumber() % 100); + const h2 = (string) => Math.abs(XXH.h32(0x1234).update(string).digest().toNumber() % 100); + const h3 = (string) => Math.abs(XXH.h32(0x6789).update(string).digest().toNumber() % 100); +const getHashes = value => [h1(value), h2(value), h3(value)]; + // fill out these two methods // `add` adds a string to the bloom filter and returns void (nothing, undefined) // `contains` takes a string and tells you if a string is maybe in the bloom filter class BloomFilter { - // you'll probably need some instance variables + constructor() { + // this.seen = []; + this.seen = new Array(100).fill(false); + } + add(string) { - // code here + getHashes(string).forEach(hash => this.seen[hash] = true) } + contains(string) { - // code here + return getHashes(string).every(hash => this.seen[hash] === true); } } // unit tests // do not modify the below code -describe.skip("BloomFilter", function () { +describe("BloomFilter", function () { let bf; + beforeEach(() => { bf = new BloomFilter(); }); - test.skip("returns false when empty", () => { + + test("returns false when empty", () => { expect(bf.contains("Brian")).toBe(false); expect(bf.contains("Sarah")).toBe(false); expect(bf.contains("Simona")).toBe(false); }); - test.skip("handles one item", () => { + + test("handles one item", () => { expect(bf.contains("Brian")).toBe(false); bf.add("Brian"); expect(bf.contains("Brian")).toBe(true); expect(bf.contains("Sarah")).toBe(false); expect(bf.contains("Simona")).toBe(false); }); - test.skip("handles many items", () => { + + test("handles many items", () => { const names = [ "Brian", "Simona", From 789dd990d8f5bcb08b4fced6f900a6bb137d699a Mon Sep 17 00:00:00 2001 From: Niels Boecker <11090412+nielsboecker@users.noreply.github.com> Date: Sun, 27 Jun 2021 12:38:12 +0100 Subject: [PATCH 4/5] pathfinding - with help --- specs/pathfinding/pathfinding.test.js | 118 +++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 3 deletions(-) diff --git a/specs/pathfinding/pathfinding.test.js b/specs/pathfinding/pathfinding.test.js index 8bbe50cc..be5a6d98 100644 --- a/specs/pathfinding/pathfinding.test.js +++ b/specs/pathfinding/pathfinding.test.js @@ -16,8 +16,120 @@ // the way I did. however feel free to use it if you'd like const logMaze = require("./logger"); +const NO_ONE = 0; +const BY_A = 1; +const BY_B = 2; + +function generateVisited(maze) { + const visited = []; + + for (let y = 0; y < maze.length; y++) { + const yAxis = []; + + for (let x = 0; x < maze.length; x++) { + const coordinate = { + closed: maze[y][x] === 1, + length: 0, + openedBy: NO_ONE, + x, + y + } + yAxis.push(coordinate); + } + + visited.push(yAxis); + } + + return visited; +} + +function getNeighbors(visited, x, y) { + const neighbors = []; + + // left? + if (y - 1 >= 0 && !visited[y - 1][x].closed) { + neighbors.push(visited[y - 1][x]); + } + + // right? + if (y + 1 < visited[0].length && !visited[y + 1][x].closed) { + neighbors.push(visited[y + 1][x]); + } + + // top + if (x - 1 >= 0 && !visited[y][x - 1].closed) { + neighbors.push(visited[y][x - 1]); + } + + // bottom + if (x + 1 < visited.length && !visited[y][x + 1].closed) { + neighbors.push(visited[y][x + 1]); + } + + return neighbors; +} + function findShortestPathLength(maze, [xA, yA], [xB, yB]) { - // code goes here + const visited = generateVisited(maze); + + const a = visited[yA][xA]; + const b = visited[yB][xB]; + + a.openedBy = BY_A; + b.openedBy = BY_B; + + let aQueue = [a]; + let bQueue = [b]; + let iteration = 0; + + // go until we find they meet, or don't meet + while (aQueue.length && bQueue.length) { + iteration++; + + // enqueue all valid A neighbors + let aNeighbors = []; + while (aQueue.length) { + const { x, y } = aQueue.shift(); + aNeighbors = aNeighbors.concat(getNeighbors(visited, x, y)); + } + + // process A neighbors + for (let i = 0; i < aNeighbors.length; i++) { + const neighbor = aNeighbors[i]; + if (neighbor.openedBy === BY_B) { + // found path! Add my current path length plus other's distance + return neighbor.length + iteration; + } else if (neighbor.openedBy === NO_ONE) { + neighbor.openedBy = BY_A; + neighbor.length = iteration; + // prepare to process this in next iteration + aQueue.push(neighbor); + } + } + + // enqueue all valid B neighbors + let bNeighbors = []; + while (bQueue.length) { + const { x, y } = bQueue.shift(); + bNeighbors = bNeighbors.concat(getNeighbors(visited, x, y)); + } + + // process B neighbors + for (let i = 0; i < bNeighbors.length; i++) { + const neighbor = bNeighbors[i]; + if (neighbor.openedBy === BY_A) { + // found path! Add my current path length plus other's distance + return neighbor.length + iteration; + } else if (neighbor.openedBy === NO_ONE) { + neighbor.openedBy = BY_B; + neighbor.length = iteration; + // prepare to process this in next iteration + bQueue.push(neighbor); + } + } + } + + return -1; } // there is a visualization tool in the completed exercise @@ -26,7 +138,7 @@ function findShortestPathLength(maze, [xA, yA], [xB, yB]) { // unit tests // do not modify the below code -describe.skip("pathfinding – happy path", function () { +describe("pathfinding – happy path", function () { const fourByFour = [ [2, 0, 0, 0], [0, 0, 0, 0], @@ -90,7 +202,7 @@ describe.skip("pathfinding – happy path", function () { // I care far less if you solve these // nonetheless, if you're having fun, solve some of the edge cases too! // just remove the .skip from describe.skip -describe.skip("pathfinding – edge cases", function () { +describe("pathfinding – edge cases", function () { const byEachOther = [ [0, 0, 0, 0, 0], [0, 2, 2, 0, 0], From 8658b980133d26719a23e9a36078a76600e568cd Mon Sep 17 00:00:00 2001 From: Niels Boecker <11090412+nielsboecker@users.noreply.github.com> Date: Sun, 27 Jun 2021 13:57:58 +0100 Subject: [PATCH 5/5] tries - with help --- specs/tries/tries.test.js | 72 ++++++++++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/specs/tries/tries.test.js b/specs/tries/tries.test.js index 14043666..19247335 100644 --- a/specs/tries/tries.test.js +++ b/specs/tries/tries.test.js @@ -13,28 +13,84 @@ const { CITY_NAMES } = require("./cities.js"); const _ = require("lodash"); // needed for unit tests class Node { - // you don't have to use this data structure, this is just how I did it - // you'll almost definitely need more methods than this and a constructor - // and instance variables + constructor(string) { + this.children = []; + this.terminus = false; + + const first = string.slice(0, 1); + const rest = string.slice(1); + + this.value = first; + if(rest.length > 0) { + this.children.push(new Node(rest)); + } else { + this.terminus = true; + } + } + + add(string) { + const first = string.slice(0, 1); + const rest = string.slice(1); + + for (let i = 0; i < this.children.length; i++) { + const child = this.children[i]; + if (child.value === first) { + if (rest) { + child.add(rest); + } else { + child.terminus = true; + } + return; + } + } + + this.children.push(new Node(string)); + } + complete(string) { - return []; + let completions = []; + + for (let i = 0; i < this.children.length; i++) { + const child = this.children[i]; + completions = completions.concat(child._complete(string, "", [])); + } + + return completions; + } + + _complete(search, built, completions) { + if (completions.length >= 3 || (search && search[0] !== this.value)) { + // wrong branch or we have enough suggestions + return completions; + } + + if (this.terminus) { + completions.push(`${built}${this.value}`); + } + + for (let i = 0; i < this.children.length; i++) { + const child = this.children[i]; + child._complete(search.substr(1), built + this.value, completions) + } + + return completions; } } const createTrie = (words) => { - // you do not have to do it this way; this is just how I did it const root = new Node(""); - // more code should go here + words.forEach(word => root.add(word.toLowerCase())); return root; }; // unit tests // do not modify the below code -describe.skip("tries", function () { +describe("tries", function () { test("dataset of 10 – san", () => { const root = createTrie(CITY_NAMES.slice(0, 10)); + console.log(root); const completions = root.complete("san"); expect(completions.length).toBe(3); expect( @@ -133,7 +189,7 @@ describe.skip("tries", function () { }); }); -describe.skip("edge cases", () => { +describe("edge cases", () => { test("handle whole words – seattle", () => { const root = createTrie(CITY_NAMES.slice(0, 30)); const completions = root.complete("seattle");