Put all words that match a criterion to a Map - javascript

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));

Related

frequentVowelCounter(word, count = {}) using recursion

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);

How to iterate through two array and compare their elements

I want to compare each element of two arrays.
Person writes down something in input. I want to compare what person had written with another phrase. The idea I took from this site https://lingua.com/german/dictation/ , you can check to understand what i want. It is kind of dictation, person writes down something and at the end he can check mistakes and so on.
I tried to realize function with two different ways, but it does not work
let [value, setValue] = useState("currentValue")
let [correctText, setCorrectText] = useState("Every day I work hard to achive my goals")
let [answer, setAnswer] = useState<string[]>([])
// first variant
let compare = () => {
let correctTextValue = correctText.split(" ")
let valueArray = value.split(" ")
let arr = []
for (let i=0; i<correctTextValue.length; i++) {
for (let j=0; j<valueArray.length; j++) {
if (correctTextValue[i]===valueArray[j]) {
arr.push(valueArray[j])
}
else {
arr.push(correctTextValue[j]
}
}
}
setAnswer([...answer, ...arr])
}
//second variant
let compare = () => {
let correctTextValue = correctText.split(" ")
let valueArray = value.split(" ")
let arr = []
for (let i=0; i<correctTextValue.length; i++) {
for (let j=0; j<valueArray.length; j++) {
if (correctTextValue[i]===valueArray[j]) {
setAnswer([...answer, valueArray[j]])
}
else {
setAnswer([...answer, correctTextValue[j]])
}
}
}
}
// this is simple jsx code
<input value={value} onChange={e=>setValue(e.target.value)}/>
<button onClick={() => compare()}>Submit</button>
<div>{answer.map(el=><span> {el} </span>)}</div>
Here's an updated version of your code that should work (if I understood correctly). It compares the words from correctText and value word by word and marks different words as isCorrect: false. It then uses that information inside the JSX to add underline styles to incorrect words.
let [value, setValue] = useState("")
let [correctText, setCorrectText] = useState("Every day I work hard to achive my goals")
let [answer, setAnswer] = useState([])
let compare = () => {
let correctTextValue = correctText.split(" ")
let valueArray = value.split(" ")
let arr = correctTextValue.map((word, index) => {
if (word === valueArray[i]) {
return {
isCorrect: true,
word: word
};
}
return {
isCorrect: false,
word: valueArray[i]
};
});
setAnswer(arr);
}
<input value={value} onChange={e=>setValue(e.target.value)}/>
<button onClick={() => compare()}>Submit</button>
<div>{
answer.map(el => {
return el.isCorrect
? <span>el.word</span>
: <span style="text-decoration:underline">el.word</span>
})
}</div>
t.fail = () => {throw Error("Unit test failed.")};
t.seqsUnequal = (a, b) => {
/* if (a === b) return false; */
let value, done;
const b_iter = b[Symbol.iterator]();
for (const a_elt of a) {
({value, done} = b_iter.next());
if (done) return "b short";
if (value !== a_elt)
return `different values ${typeof value}:${typeof a_elt}`;
};
({value, done} = b_iter.next());
if (! done) return "a short";
return false
};
t.seqsEqual = (a, b) => ! t.seqsUnequal(a, b);
t.exp_seq_0 = ["Greens"];
if (! t.seqsEqual(t.exp_seq_0, t.exp_seq_0)) t.fail();
if (! t.seqsEqual([], [])) t.fail();
if (t.seqsEqual(["foo"], [])) t.fail();
if (t.seqsEqual([], [0])) t.fail();
if (! t.seqsEqual(["fox", "went", "out", "on", "a", "winter's", "night"]
, ["fox", "went", "out", "on", "a", "winter's", "night"])) t.fail();
The above code will shallow-compare any two iterables for equality. But given that you know that you have arrays, you can use indexing, for a much simpler solution.

how many items inside list contains a word

let list: Array<string> = ['abc', 'efg', 'abcde', 'eefg'];
I wonder how can I check how many items inside list contains abc (a count).
I know that I can use indexOf inside loop but I wonder if there any other, easier, convenient way.
Here'a solution with reduce()
const list = ['abc', 'efg', 'abcde', 'eefg']
const countReduce = list.reduce((count, item) => {
return count += item.includes('abc') ? 1 : 0
}, 0)
console.log('reduce:', countReduce)
And here's one with regexp:
const list = ['abc', 'efg', 'abcde', 'eefg']
const count = (list.join(',').match(/abc/g) || []).length;
console.log(count);
And still a string concatenation style:
const list = ['abc', 'efg', 'abcde', 'eefg']
const count = list.join(',').split('abc').length - 1
console.log(count);
A bit more complicated one - treating the string as an array (and NOT using indexOf... :) )
const list = ['abc', 'efg', 'abcdeabc', 'eefg']
let count = 0
for (let item of list) {
let onlyOnce = 0 // guarding that the string is counted inly once/item
const length = item.length
for (let i = 0; i < length; i++) {
if (item[i] + item[i + 1] + item[i + 2] == 'abc' && !onlyOnce) {
count++
onlyOnce++
}
}
}
console.log(count)
A simple way is to filter the list and get the length:
let count = list.filter(s => s.includes(word)).length;
Using indexOf:
let count = list.filter(s => s.indexOf(word) > -1).length;
Try this
function isEqualTo(value) {
return value.includes("abc"); // or return value == "abc"; // whatever you want
}
var words = list.filter(isEqualTo).length

JS How to for palindrome [duplicate]

This question already has answers here:
Palindrome check in Javascript
(45 answers)
Closed 4 years ago.
The question I have been given is this;
create a function that takes an array of words and returns an array containing only the palindromes.
A palindrome is a word that is spelled the same way backwards.
E.g. ['foo', 'racecar', 'pineapple', 'porcupine', 'pineenip'] => ['racecar', 'pineenip']
This is the code that I create;
let arr = []
let str = words.slice(0)
let pal = str.toString().split("").reverse().join("")
console.log(pal);
for (let i = 0; i < words.length; i++) {
for (let k = 0; k < pal.length; k++) {
if (words[i] == pal[k]) {
arr.push(words[i])
}
}
}
return arr
}
This is the test that my code is run against;
describe("findPalindromes", () => {
it("returns [] when passed []", () => {
expect(findPalindromes([])).to.eql([]);
});
it("identifies a palindrom", () => {
expect(findPalindromes(["racecar"])).to.eql(["racecar"]);
});
it("ignores non-palindromes", () => {
expect(findPalindromes(["pineapple", "racecar", "pony"])).to.eql([
"racecar"
]);
});
it("returns [] when passed no palindromes", () => {
expect(findPalindromes(["pineapple", "watermelon", "pony"])).to.eql([]);
});
});
Does anyone have any any suggestion of how to make my code work?
This is the simplest function that returns true or false if the str is a palindrome or not.
I would use this in combination with the filter function to filter on all palindromes. Like this
function checkPalindrom(str) { //function that checks if palindrome or not
return str == str.split('').reverse().join('');
}
const result = words.filter(word => checkPalindrom(word)); //filter function that filters array to only keep palindromes
Without giving spoilers to the answer (this is a common interview question) a clean approach would be as follows:
Define a function isPalindrome(string): boolean
Use the filter property available on the Array prototype to return an array of only palindromes e.g. inputArray.filter(isPalindrome)
Both can be unit tested separately, for example:
You could define an array of inputs and expected outputs for isPalindrome [{ input: "racecar", expectedOutput: true}, {input: "pineapple", expectedOutput: false}, ...] and loop over each test case.
function isPalindrome(word) {
const firstHalf = word.slice(0, Math.ceil(word.length/2));
const secondHalfReversed = word.slice(Math.floor(word.length/2)).split('').reverse().join('');
return firstHalf === secondHalfReversed;
}
function getPalindromesFromArray(arr) {
return arr.filter(isPalindrome);
}
const wordsArr = ['foo', 'racecar', 'pineapple', 'porcupine', 'pineenip'];
console.log(getPalindromesFromArray(wordsArr));
using for loop and filter
let arr = ["foo", "racecar", "pineapple", "porcupine", "pineenip",'pap','aaaa'];
let palindromes = arr.filter(w => {
let len = w.length;
for (let i = 0; i < len / 2; i++) {
if (w[i] == w[len - i - 1]) {
return true;
} else {
return false;
}
}
});
console.log(palindromes)
To solve that first I would create an isPalindrome function like this:
function isPalindrome(word) {
palindromeWord = ''
for(var i = word.length - 1; i >= 0; i--) {
palindromeWord += word.charAt(i)
}
return palindromeWord === word
}
and then I would check for each word inside the array like this:
let arr = ['foo', 'racecar', 'pineapple', 'porcupine', 'pineenip']
let palindromeArr = []
arr.forEach(word => {
if (isPalindrome(word)) {
palindromeArr.push(word)
}
})
console.log(palindromeArr)
What you have is good, however when you did
var pal = str.toString().split("").reverse().join("")
You changed from an array to a string, then you went into the loop with the string, so pal[k] gave a character and not a word.
To change pal back to an array of strings, split it again, use
var pal = str.toString().split("").reverse().join("").split(",");
var words = ['foo', 'racecar', 'pineapple', 'porcupine', 'pineenip'];
var arr = [];
var str = words.slice(0);
var pal = str.toString().split("").reverse().join("").split(",");
console.log(pal);
for (let i = 0; i < words.length; i++) {
for (let k = 0; k < pal.length; k++) {
if (words[i] == pal[k]) {
arr.push(words[i])
}
}
}
console.log(arr);

regex to find pairs in array

I would like to parse that string:
[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]
In order to have a key value (JSON)
{
"abc.d.2": "mcnv.3.we",
"abec.d.2: "mcnv.4.we",
"abhc.d.2": "mcnv.5.we"
}
First I would like to check if string can be parse to make it key=>value.
How can I check the string if it contains pairs?
Thanks
You can try something like this:
Approach 1:
Idea:
Update the regex to have more specific characters. In your case, alphanumeric and period.
Get all matching elements from string.
All odd values are keys and even matches are values.
Loop over matches and create an object.
const str = "[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]";
const matches = str.match(/[\w\.\d]+/gi);
const output = {};
for(var i = 0; i< matches.length; i+=2) {
output[matches[i]] = matches[i+1];
}
console.log(output)
Approach 2:
Idea:
Write a regex to capture each individual group: [...:...]
Then eliminate braces [ and ].
Split string using comma ,.
First part is your key. Second is your value.
const str = "[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]";
const matches = str.match(/\[([\w\d\.,]*)\]/gi);
const output = matches.reduce((obj, match) => {
const parts = match.substring(1, match.length - 1).split(',');
obj[parts[0]] = parts[1];
return obj;
}, {})
console.log(output)
In above approach, you can also include Map. The iteration can be bit confusing initially, but you can try.
const str = "[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]";
const matches = str.match(/\[([\w\d\.,]*)\]/gi);
const output = matches.reduce((obj, match) => {
const parts = match.substring(1, match.length - 1).split(',');
obj.set(...parts)
return obj;
}, new Map())
for (const [k, v] of output.entries()) {
console.log(`Key: ${k}, value: ${v}`)
}
Parse the array as JSON, iterate over the array, adding entries to the target object as you go, watch out for duplicate keys:
let dict_target = {}; // The target dictionary,
let src, arysrc, proceed = false;
try {
src = "[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]"
.replace(/,/g, '","')
.replace(/\]","\[/g, '"],["')
.replace(/^\[\[/, '[["')
.replace(/\]\]$/, '"]]')
;
arysrc = JSON.parse(src);
proceed = true; // Could parse the data, can carry on with processing the data
} catch (e) {
console.log(`Source data unparseable, error '${e.message}'.`);
}
if (proceed) {
arysrc.forEach ( (a_item, n_idx) => {
if (dict_target.hasOwnProperty(a_item[0])) {
// add any tests and processing for duplicate keys/value pairs here
if (typeof dict_target[a_item[0]] === "string") {
dict_target[a_item[0]] = [ dict_target[a_item[0]] ];
}
dict_target[a_item[0]].push(a_item[1]);
}
else {
dict_target[a_item[0]] = a_item[1];
}
});
} // if -- proceed
My coding golf solution...
const parse = (str) => {
let obj = {};
str.replace(
/\[([^\[,]+),([^\],]+)\]/g,
(m, k, v) => obj[k] = v
);
return obj;
};
Advantages:
More Permissive of arbitrary chars
More Tolerant of missing values
Avoids disposable objects for GC
Disadvantages:
More Permissive of arbitrary chars!
This is not a proper parser...
Does not have context, just [key,val]
I actually wanted to post the following as my answer... but I think it'll get me in trouble :P
const parse=(str,obj={})=>
!str.replace(/\[([^\[,]+),([^\],]+)\]/g,(m,k,v)=>obj[k]=v)||obj;
Here's the code which validates the string first and outputs the result. Not at all optimal but does the task just fine.
var string = '[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]';
var result = (/^\[(\[.*\..*\..*\,.*\..*\..*\]\,)*\[(.*\..*\..*\,.*\..*\..*)\]\]$/g).exec(string);
if (result) {
var r1 = result[1].replace(/\[|\]/g, '').split(',');
var r2 = result[2].split(',');
var output = {};
for (var i = 0; i < r1.length -1; i +=2) {
output[r1[i]] = r1[i+1];
}
output[r2[0]] = r2[1];
console.log(output);
} else {
console.log('invalid string');
}

Categories

Resources