Skip to content

Update bubble-sort and radix-sort solutions #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions specs/bloom-filters/bloom-filters.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion specs/bubble-sort/bubble-sort.solution.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
118 changes: 115 additions & 3 deletions specs/pathfinding/pathfinding.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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],
Expand Down Expand Up @@ -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],
Expand Down
6 changes: 3 additions & 3 deletions specs/radix-sort/radix-sort.solution.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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));
});
});
72 changes: 64 additions & 8 deletions specs/tries/tries.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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");
Expand Down