Skip to content
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
132 changes: 128 additions & 4 deletions src/adagrams.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,139 @@
const LETTER_POOL = {
A: 9,
B: 2,
C: 2,
D: 4,
E: 12,
F: 2,
G: 3,
H: 2,
I: 9,
J: 1,
K: 1,
L: 4,
M: 2,
N: 6,
O: 8,
P: 2,
Q: 1,
R: 6,
S: 4,
T: 6,
U: 4,
V: 2,
W: 2,
X: 1,
Y: 2,
Z: 1,
};

const LETTER_SCORE = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great use of const! Since these constants at the top of the file are only accessed by one function each, it would be reasonable to place the objects inside the functions rather than as a constant. There are tradeoffs, the structure would clutter the function some, but it keeps the data as close as possible to where it's being used, and would mean that other functions who shouldn't need it couldn't access it.

AEIOUNRST: 1,
DG: 2,
BCMP: 3,
FHVWY: 4,
K: 5,
JX: 8,
QZ: 10,
};

export const drawLetters = () => {
// Implement this method for wave 1
// # not sure why not be able to use deep copy.
// const copy_pool = structuredClone(LETTER_POOL);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was happening, were you getting a particular error?

const copyPool = { ...LETTER_POOL };
let randomTenLetters = [];
const keysArr = Object.keys(copyPool);

let randomKey;
while (randomTenLetters.length < 10) {
randomKey = keysArr[Math.floor(Math.random() * keysArr.length)];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice approach for getting a random index.

This will give us an even chance of picking any single letter in the alphabet without going over the number of each tile we have. This is slightly different than what the README asks - we won't accurately represent the distribution of tiles because we pick a letter from 1-26, when the chances of picking some letters should be higher than others. How could we update the algorithm to account for this?

if (copyPool[randomKey] > 0) {
randomTenLetters.push(randomKey);
copyPool[randomKey] -= 1;
}
}
return randomTenLetters;
};

export const usesAvailableLetters = (input, lettersInHand) => {
// Implement this method for wave 2
const inputArr = input.split("");
if (inputArr.length > lettersInHand.length) {
return false;
}

for (let i = 0; i < inputArr.length; ++i) {
if (lettersInHand.includes(inputArr[i]) === false) {
return false;
}
let index = lettersInHand.indexOf(inputArr[i]);
lettersInHand.splice(index, index + 1);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We often don't want to edit mutable parameters because those changes would be seen outside the function call. I would recommend making a copy of lettersInHand that we can remove letters from as we loop.

}
Comment on lines +64 to +70

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we needed to reduce the time complexity of our solution, another approach could be to build a frequency map of our hand then loop over the characters in the input, checking if the character is in our frequency map, and if it is, then check the value to see if there are still tiles left in our hand for that letter.

return true;
};

export const scoreWord = (word) => {
// Implement this method for wave 3
// if word is empty str return 0
if (word === "") return 0;

word = word.toUpperCase();
let total = 0;
let keysArr = Object.keys(LETTER_SCORE);

for (let i = 0; i < word.length; ++i) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we won't be reassigning the loop variable in the body of a loop, I recommend using const over let to declare the variable.

for (let j = 0; j < keysArr.length; ++j) {
if (keysArr[j].indexOf(word[i]) !== -1) {
total += LETTER_SCORE[keysArr[j]];
}
}
}
Comment on lines +80 to +88

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could simplify this loop a little with a for...of loop:

for (const letter of word) {
    for (const [key, value] of Object.entries(LETTER_SCORE)) {
        if (key.indexOf(letter) !== -1) {
            total += value;
        }
    }
}

In the worst case scenario, for every letter in word, we might need to search every letter in every key in LETTER_SCORE. Is there a way we could restructure the code or the data structure that holds LETTER_SCORE to reduce the time complexity?


if (word.length >= 7) {
total += 8;
}
return total;
};

export const highestScoreFrom = (words) => {
// Implement this method for wave 4
let words_socres_Obj = {};
let maxScore = 0;

// find each word's score and the max score
for (let i = 0; i < words.length; ++i) {
let eachScore = scoreWord(words[i]);
words_socres_Obj[words[i]] = eachScore;
if (eachScore > maxScore) maxScore = eachScore;
}

// loop through the words_socres_obj to get the sames max score words
for (let eachWord in words_socres_Obj) {
if (words_socres_Obj[eachWord] !== maxScore) {
delete words_socres_Obj[eachWord];
}
}

let result = { word: "", score: 0 };

if (Object.keys(words_socres_Obj).length > 1) {
let wordsArr = Object.keys(words_socres_Obj);
let wordsLengthArr = wordsArr.map((word) => word.length);
let minLengh = Math.min(...wordsLengthArr);

// if the words is 10 letters => choose the 10 letters (find)
if (wordsArr.find((word) => word.length === 10) !== undefined) {
let tenLettersWord = wordsArr.find((word) => word.length === 10);
result.word = tenLettersWord;
result.score = words_socres_Obj[tenLettersWord];
return result;
}
// if words different length => pick the shortest word (min)
// if words in same length => pick the first (if all the same min will retrun the only num)
let minOrfirstWord = wordsArr.find((word) => word.length === minLengh);
result.word = minOrfirstWord;
result.score = words_socres_Obj[minOrfirstWord];
return result;
} else {
result.word = Object.keys(words_socres_Obj)[0];
result.score = Object.values(words_socres_Obj)[0];
return result;
}
};
22 changes: 15 additions & 7 deletions test/adagrams.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,26 @@ const LETTER_POOL = {
describe("Adagrams", () => {
describe("drawLetters", () => {
it("draws ten letters from the letter pool", () => {
//Act
const drawn = drawLetters();

//Assert
expect(drawn).toHaveLength(10);
});

it("returns an array, and each item is a single-letter string", () => {
//Act
const drawn = drawLetters();

//Assert
expect(Array.isArray(drawn)).toBe(true);
drawn.forEach((l) => {
expect(l).toMatch(/^[A-Z]$/);
});
});

it("does not draw a letter too many times", () => {
//Act
for (let i = 0; i < 1000; i++) {
const drawn = drawLetters();
const letter_freq = {};
Expand All @@ -63,6 +68,7 @@ describe("Adagrams", () => {
}
}

//Assert
for (let letter of drawn) {
expect(letter_freq[letter]).toBeLessThanOrEqual(LETTER_POOL[letter]);
}
Expand Down Expand Up @@ -120,7 +126,9 @@ describe("Adagrams", () => {
});

it("returns a score of 0 if given an empty input", () => {
throw "Complete test";
expectScores({
"": 0,
});
});

it("adds an extra 8 points if word is 7 or more characters long", () => {
Expand All @@ -133,19 +141,19 @@ describe("Adagrams", () => {
});
});

describe.skip("highestScoreFrom", () => {
describe("highestScoreFrom", () => {
it("returns a hash that contains the word and score of best word in an array", () => {
const words = ["X", "XX", "XXX", "XXXX"];
const correct = { word: "XXXX", score: scoreWord("XXXX") };
const words = ["a", "DOG", "king", "Quit"];
const correct = { word: "Quit", score: scoreWord("Quit") };

expect(highestScoreFrom(words)).toEqual(correct);
});

it("accurately finds best scoring word even if not sorted", () => {
const words = ["XXX", "XXXX", "X", "XX"];
const correct = { word: "XXXX", score: scoreWord("XXXX") };
const words = ["king", "Quantizers", "a", "DOG"];
const correct = { word: "Quantizers", score: scoreWord("Quantizers") };

throw "Complete test by adding an assertion";
expect(highestScoreFrom(words)).toEqual(correct);
});

describe("in case of tied score", () => {
Expand Down
Loading