Learning from my mentors
Mentors, guidance and inspiration.
Throughout my career I have had good and bad experiences that have taught me a lot. Finding a mentor is somewhat difficult, and finding a good one is even harder. I have been lucky enough to have several. I especially remember my undergraduate and master's thesis supervisor, Dr. María Elena Martínez, and the team at my first job in Canada: SourceKnowledge. Thanks to them, settling in was much easier. Outside my professional life, I have also been fortunate to have friends who have been excellent mentors.
During the month-long Design your environment to achieve your goals challenge with Benjamin Hardy and Jonathan Levi, the importance of mentors became even clearer to me. A mentor helps us discover our weaknesses and improve our skills. It is truly a win-win relationship. The learner encounters new ideas while also contributing interesting ideas to their mentor. The mentor needs a solid grasp of the topic to explain it simply and effectively. That is why we should actively look for mentors rather than leave everything to chance. After searching for a while, I finally found a place that works well for my current circumstances. Thanks to Exercism, I found a mentor who helps me improve my JavaScript skills.
Over the last week I have learned a lot from my mentor ghjk. Our exchange while I was working on the Pangram exercise was genuinely interesting and productive. A pangram is a text that uses every letter in a language's alphabet.
Original solution
export const isPangram = sentence => {
const alphabet = new Set('abcdefghijklmnopqrstuvwxyz');
[...sentence.toLowerCase()].forEach(function (letter) {
if (alphabet.has(letter)) {
alphabet.delete(letter);
}
});
return alphabet.size === 0;
};
I create a Set containing every letter of the alphabet, then iterate through the function's input text. If a letter is in the alphabet, I remove it. At the end of the loop, the text is a pangram if the alphabet is empty. I was happy with my solution.
Improvements suggested by my mentor
This solution works correctly, but it can be improved. My mentor pointed out that I declared the alphabet as a constant and then modified it. He recommended two ways to solve the exercise without modifying the alphabet: one using every, and another using a Set as in my solution. Instead of building it from the alphabet and removing letters found in the input, we could build it from the input and filter it using the alphabet's letters. If the resulting Set contains 26 elements, the text is a pangram.
Constants in JS
I was not entirely clear about how constants work in JS. Honestly, I had not read that section of the manual; I had only seen examples and assumed they were very similar to PHP constants. I thought their type could not change but their value could. I asked my mentor to explain the difference between let and const, since both allow me to modify an object. He explained that for primitive types such as number / boolean / string, we cannot change a constant's value. For example:
const someString = 'Hello, world!';
someString = 'Another string'; // TypeError: invalid assignment to const 'someString'
For objects, however, it is different because the value is a reference to their contents. We cannot assign a new value to that reference, but changing an element of the object is perfectly valid.
const someArray = [1, 2, 3];
someArray = [ 4, 5, 6 ]; // TypeError: invalid assignment to const 'someArray'
someArray[0] = 0; // OK -> Array(3) [ 0, 2, 3 ]
His rule is to use let if he knows he will modify the object later, and const otherwise. JS constants are an interesting topic with many details. Reading their documentation is a good idea: Constants.
First iteration
Based on his recommendations, I changed const to let and implemented two new solutions:
Solution 1
export const isPangramV2 = sentence => {
const lowerCaseSentence = sentence.toLowerCase();
return alphabetArray.every(function (letter) {
return lowerCaseSentence.indexOf(letter) !== -1;
})
};
const alphabetArray = [...'zqjxkvbywgpfmucdlhrsnioate'];
This function checks that every letter of the alphabet appears in the text. Here I had the idea of ordering the letters by increasing letter frequency, so it would be quicker to find texts missing a letter.
Solution 2
export const isPangram = sentence => 26 === (new Set([...sentence.toLowerCase()].filter(character => alphabet.indexOf(character) !== -1)).size);
const alphabet = 'abcdefghijklmnopqrstuvwxyz';
This solution is much more concise and, I believe, more efficient at finding pangrams. We filter the text's characters to keep only those in the alphabet and then create a Set from them. If its size is 26, the text contains every letter. Once again I was very happy with my solution, and once again I learned it could still be improved.
Improvements suggested by my mentor
For Solution 1, my mentor recommended extracting the alphabet's letters directly from the input text. That way I would not need to convert the input text into an array, making the code even more concise.
For Solution 2, he mentioned that includes expressed the intent more clearly, but indexOf was faster.
Final iteration
Solution 1
export const isPangram = sentence => 26 === (new Set(sentence.toLowerCase().match(/[a-z]/g)).size);
Solution 2
export const isPangramV2 = sentence => {
const lowerCaseSentence = sentence.toLowerCase();
return alphabet.every(function (letter) {
return lowerCaseSentence.includes(letter);
})
};
const alphabet = [...'zqjxkvbywgpfmucdlhrsnioate'];
Thanks to ghjk and all the mentors who help me improve every day.
References
- SourceKnowledge
- Superhuman Mastermind challenge: Design your environment to achieve your goals
- Benjamin Hardy
- Jonathan Levi (Superhuman Academy)
- Exercism
- Wikipedia: Pangram definition
- JavaScript Set
- JavaScript Array every
- JavaScript constants
- Letter frequency
- JavaScript String includes
- JavaScript String indexOf