frequentVowelCounter(word, count = {}) using recursion - javascript

I can do this problem using a for loop, but I'm having trouble using the recursive method.
I want to count how many times each vowel is called and return the most frequent vowel in an array.
If the array does not have a vowel to return is a string.
const vowel = ['a', 'e', 'i', 'o', 'u'];
frequentVowelCounter(word, count = {})
frequentVowelCounter(['cat', 'rain', 'dorm', 'apple', 'sun'])); // 'a'
I tried writing a base case:
if (word.length != vowel)
return ''
I don't think it's right, I'm just stuck.

Why would you want to make this recursive? An iterative approach works well.
You will need to loop over each word, and for each word, its letters. As you loop over each letter, you check to see if it is a vowel. If it is a vowel, you increment the frequency map i.e byLetter.
After you build the letter frequency map, you will need to invert the map into a count map i.e. byCount.
Finally, you can get the keys of the byCount map to find the max count and return the value for the corresponding entry in the map.
Note: You may have more than one letter share the same max occurrence count. This is why the result of the function in an array.
const maxFrequency = (words, letters) => {
const
letterSet = new Set(letters),
byLetter = new Map(),
byCount = new Map();
for (let word of words) {
for (let letter of word.toLowerCase().split('')) {
if (letterSet.has(letter)) {
byLetter.set(letter, (byLetter.get(letter) ?? 0) + 1);
}
}
}
for (let [letter, count] of byLetter) {
const letters = byCount.get(count) ?? new Set();
byCount.set(count, letters.add(letter));
}
return [...byCount.get(Math.max(...byCount.keys()))];
};
const
vowels = ['a', 'e', 'i', 'o', 'u'],
words = ['cat', 'rain', 'dorm', 'apple', 'sun'],
mostFrequent = maxFrequency(words, vowels);
console.log(...mostFrequent); // ['a']
If you want to do this recursively, just join all the words together and iterate through the string starting at index 0. The helper function i.e. __helperFn should never be called directly.
Note: You will need a helper function to set up and perform the recursion.
const __helperFn = (str, letterSet, frequency) => {
if (str.length === 0) {
const max = Math.max(...Object.values(frequency));
return Object.entries(frequency)
.filter(([letter, count]) => count === max)
.map(([letter]) => letter);
}
const letter = str.charAt(0);
if (letterSet.has(letter)) {
frequency[letter] = (frequency[letter] ?? 0) + 1;
}
return __helperFn(str.slice(1), letterSet, frequency);
}
const maxFrequency = (words, letters) =>
__helperFn(words.join('').toLowerCase(), new Set(letters), {});
const
vowels = ['a', 'e', 'i', 'o', 'u'],
words = ['cat', 'rain', 'dorm', 'apple', 'sun'],
mostFrequent = maxFrequency(words, vowels);
console.log(...mostFrequent); // ['a']

This should work:
let w = ["cat", "rain", "dorm", "apple", "sun"];
const vowelCounter = (words) => {
let vowels = {
a: 0,
e: 0,
i: 0,
o: 0,
u: 0,
};
const count = (char) => {
if (vowels.hasOwnProperty(char)) {
vowels[char]++;
}
};
const rekursive = (words, index = 0) => {
if (index === words.length) {
return;
}
let i = 0;
while (true) {
if (i >= words[index].length) {
break;
}
try {
count(words[index].charAt(i));
} catch (error) {}
i++;
}
rekursive(words, ++index);
};
rekursive(words);
console.log(vowels);
};
vowelCounter(w);

Related

Put all words that match a criterion to a Map

I'm really new at JavaScript and I faced the first issue that I'm trying to solve.
The goal of a program is to use first letter of each word as a key and the unique words as a values.
Here is the code:
function sortToMap(str){
let lowerString = str.toLowerCase();
let result = lowerString.split(" ");
let myMap = new Map();
for(let i = 0; i < result.length; i++){
myMap.set(result[i][0], result[i]);
}
return myMap;
}
let myString = "Test string to check How it Works and hopefully it is fine";
console.log(sortToMap(myString));
So it looks like this:
(Actual result)
Map(8) {
't' => 'to',
's' => 'string',
'c' => 'check',
'h' => 'hopefully',
'i' => 'is',
'w' => 'works',
'a' => 'and',
'f' => 'fine'
}
(Expected result)
Map(8) {
't' => 'test', 'to',
's' => 'string',
'c' => 'check',
'h' => 'hopefully',
'i' => 'it', 'is',
'w' => 'works',
'a' => 'and',
'f' => 'fine'
}
I'm trying to figure out what can I do to achieve the expected result. Are there any suggestions?
The value should be an array of words. Check if the map entry exists. If it does, push onto it, otherwise create it.
function sortToMap(str) {
let lowerString = str.toLowerCase();
let result = lowerString.split(" ");
let myMap = new Map();
for (let i = 0; i < result.length; i++) {
let initial = result[i][0];
if (myMap.has(initial)) {
if (!myMap.get(initial).includes(result[i])) {
myMap.get(initial).push(result[i]);
}
} else {
myMap.set(initial, [result[i]]);
}
}
return myMap;
}
let myString = "Test string to check How it Works and hopefully it is fine";
console.log(Object.fromEntries(sortToMap(myString)));
Instead of setting the word as the value, you should create an array that stores all the words starting with a given letter.
Here is a working code :
function sortToMap(str) {
const words = str.toLowerCase().split(" ")
// no need for map in JS, an object will work fine
const map = {}
for(let word of words) {
const key = word[0]
if(!(key in map))
map[key] = []
map[key].push(word)
}
return map
}
let myString = "Test string to check How it Works and hopefully it is fine";
console.log(sortToMap(myString));

Js secret santa claus algoritm

I wanted to make a small script in js that having a list of users, one user has to make a gift to another.
By applying the following constraints:
If "a" is the santa claus and gives a gift to "c" it cannot be the other way around.
So "c" cannot be the santa claus of "a".
It must work with both an even and an odd number of users.
In your opinion, what could be the right approach to use to try to minimize the number of comparisons, that is, speed up the script.
I was thinking something like this to start, but afterwards I'm not sure how to proceed:
let name = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let a = [...name];
let group1 = [];
let groupSanta = [];
let groupUser = [];
for (var i = 0; i < name.length / 2 - 1; i++) {
let santaClaus = a[Math.floor(Math.random() * a.length)];
a = a.filter(item => item !== santaClaus);
let user = a[Math.floor(Math.random() * a.length)];
a = a.filter(item => item !== user);
group1.push({ santaClaus, user });
}
console.log(a, group1);
You can just randomly sort the array and assign each person to the next one. Then assign the first person to the last one in the array
// Define names
const names = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
// Function to shuffle array
const shuffle = (arr) => {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
const randomNames = shuffle(names);
// Match each person with the next one, folding over at the end
const matches = randomNames.map((name, index) => {
return {
santa: name,
receiver: randomNames[index + 1] || randomNames[0],
}
});
console.log(matches);
let players = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
players = shuffleArray(players)
const matches = players.map((name, index) => {
return {
santa: name,
receiver: players[index + 1] || players[0],
}
});
function shuffleArray(array) {
let currentIndex = array.length, randomIndex
while (currentIndex != 0) {
randomIndex = Math.floor(Math.random() * currentIndex)
currentIndex--
[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]
}
return array
}
console.log(matches)

How to modify my code to get all non-unique values?

With duplicates, this code worked fine
const findDuplicates = (word) => {
let arr = word.toLowerCase();
let sorted_arr = [...arr].slice().sort();
console.log(sorted_arr);
let results = [];
for (let i = 0; i < sorted_arr.length - 1; i++) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
results.push(sorted_arr[i]);
}
}
return results;
}
console.log(findDuplicates('piccdda123dd'));
Output
[
'1', '2', '3', 'a',
'c', 'c', 'd', 'd',
'd', 'd', 'i', 'p'
]
[ 'c', 'd', 'd', 'd' ]
How to modify if condition to deal with multiple non-unique values?
For something like this we should be gunning for constant time 0(n). Sorting is going to give us O(nlogn) at best and is not required.
function findDuplicates(s) {
const seen = {};
const result = [];
for (let c of s.toLowerCase()) {
if(c in seen) {
result.push(c);
}
seen[c] = true; // could be any value doesn't have to be boolean.
}
return result;
}
console.log(findDuplicates('piccdda123dd'));
result
[ 'c', 'd', 'd', 'd' ]
We just need to iterate over the string once and keep a map object (could use es6 Set) that tells us if we've seen this value before. If we see it again and it's already been seen we can append it to the result array.
If you wanted to see each duplicated character only once try
function findDuplicates2(s) {
const seen = {};
for (let c of s.toLowerCase()) {
const ent = seen[c];
seen[c] = (ent === undefined) ? 1 : ent + 1;
}
return Object.entries(seen).filter(([k,v]) => v > 1).map(([k,v]) => k);
}
console.log(findDuplicates2('piccdda123dd'));
// prints ['c', 'd']
SOLVED.
const findDuplicates = (word) => {
let arr = word.toLowerCase();
let sorted_arr = [...arr].slice().sort();
console.log(sorted_arr);
let results = [];
for (let i = 0; i < sorted_arr.length - 1; i++) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
if (!results.includes(sorted_arr[i]))
results.push(sorted_arr[i]);
}
}
return results;
}
ES6 includes IS REALLY powerfull.

Counting vowels in a string JS

What is the best way to count each vowel separately in a string and display it as "A=0", "i=2", "e=1", and so on, case insensitively? I have this so far, but it doesn't count each symbol, only the total amount.
let arr_vowels = 'aeiouAEIOU'.split('');
let count = 0;
word.split('').forEach(function(e) {
if (arr_vowels.indexOf(e) !== -1) {
count++;
}
});
console.log(count);
With the current approach, you are increasing the same count for a vowel.
One approach can be using a Map for example counting each vowel separately, and then loop the entries of the Map to get the key and value.
const vowels = 'aeiouAEIOU';
const str = "This is A test for counting vowels.";
const m = new Map();
for (let i = 0; i < str.length; i++) {
let char = str[i]
if (vowels.includes(char)) {
!m.has(char) ? m.set(char, 1) : m.set(char, m.get(char) + 1);
}
}
for (const [key, value] of m.entries()) {
console.log(`${key} = ${value}`);
}
Similar to the other solution, but simply with an object, you can do the following:
const vowels = {
a: 0,
e: 0,
i: 0,
o: 0,
u: 0,
};
const str = "This is A test for counting vowels.";
str.split('').forEach((letter) => {
const insensitiveLetter = letter.toLowerCase();
if (vowels[insensitiveLetter] !== undefined) {
vowels[insensitiveLetter] += 1;
}
});

Javascript assign and increment Obj's key value(with Reduce)

Im following a course online and one of the challenges is this:
Write a function called vowelCount that accepts a string and returns an object with each key being the vowel and the value being the number of times the vowel occurs in the string (the order of keys in the object does not matter).
vowelCount('incredible');
// {i:2, e: 2}
vowelCount('awesome');
// {a:1, e:2, o:1}
So far, I've come up with the following code, using Javascript's reduce:
function vowelCount(word) {
var vowels = ['a', 'e', 'i', 'o', 'u'];
var final = word.split('').reduce(function(obj, val, index) {
if (vowels.indexOf(val) > -1) {
//obj[val] = 0
obj[val]++;
}
return obj
}, {})
console.log(final)
}
I think I'm close but I'm having trouble wrapping my head on how to assign and increment the numerical value of the vowel key at point of checking if its a vowel. I tried instantiating the value to 0, but that only keeps the value at 1.
Use short circuit evaluation to check if the value exists, and if not use 0 instead:
console.log(vowelCount('incredible')); // {i:2, e: 2}
console.log(vowelCount('awesome')); // {a:1, e:2, o:1}
function vowelCount(word) {
var vowels = ['a', 'e', 'i', 'o', 'u'];
return word.split('').reduce(function(obj, val, index) {
if (vowels.indexOf(val) > -1) {
obj[val] = (obj[val] || 0) + 1;
}
return obj;
}, {});
}
In addition, instead of using an array of vowels and the Array.indexOf() check, you can initialize the result object with the vowels, and increment them directly:
console.log(vowelCount('incredible')); // {i:2, e: 2}
console.log(vowelCount('awesome')); // {a:1, e:2, o:1}
function vowelCount(word) {
return word.split('').reduce(function(obj, val) {
if(val in obj) {
obj[val]++;
}
return obj;
}, { a: 0, e: 0, i: 0, o: 0, u: 0 });
}
I think this is actually a bad usecase for reduce. Might just use a regular for loop and use the or operator (||) to replace undefined with 0:
function vowelCount(word){
const vowels = "aeiou";
const result = {};
for(const char of word)
if(vowels.includes(char))
result[char] = (result[char] || 0) + 1;
return result;
}
If that is too spooky you could just check if char already exists in result and set it otherwise:
function vowelCount(word){
const vowels = "aeiou";
const result = {};
for(const char of word){
if(vowels.includes(char)){
if(!result[char]) result[char] = 0;
result[char]++;
}
}
return result;
}
You were very close! It was because you were trying to increment undefined. You must set that value equal to a number on the accumulator before you increment it.
function vowelCount(word) {
var vowels = ['a', 'e', 'i', 'o', 'u'];
var final = word.split('').reduce(function(obj, val, index) {
if (vowels.indexOf(val) > -1) {
// if we have seen the vowel, we increment it. Otherwise, it is the first time.
obj[val] ? obj[val]++ : obj[val] = 1;
}
return obj
}, {})
console.log(final)
}

Categories

Resources