Issue with nested for loops in Javascript - Basic Algorithm Scripting - javascript

I am trying to write a function that accepts an array of strings as an argument. I want to return the array of strings sorted first by number of vowels and then alphabetically.
Input: ['dog', 'cat', 'elephant', 'hippo', 'goat']
Output: ['cat', 'dog', 'goat', 'hippo', 'elephant']
function vowelMatching(vowels) {
return vowels.match(/[aeiou]/ig) || [];
//The 'i' in regex accounts for capitals
}
function arrangeByVowels(array) {
const outputArray = [];
console.log(array.length)
console.log(outputArray.length)
for(let x=0; array.length === outputArray.length; x++) {
// let x = 0
const filterArray = [];
for(let i =0; i<array.length; i++) {
//console.log(vowelMatching(array[i]).length)
if (vowelMatching(array[i]).length === x) {
filterArray.push(array[i])
}
}
//console.log(filterArray.sort())
outputArray.push(...filterArray.sort())
}
return outputArray
}
console.log(arrangeByVowels(['dog', 'gAg', 'qq', 'cat', 'elephant', 'hippo', 'goat']))
I am using a nested for loop to achieve this. If I assign a value to x for example let x = 1 and I comment out the outer for loop, it will return all the words with 1 vowel in alphabetical order. (This is the result I am after)
I want to loop through x until array.length === outputArray.length but at the moment this code is returning an empty array, and I am not sure why.
I think I am close, but have no idea where I am going wrong. What am I doing wrong?

As a follow up answer to your actual question....
The issue is the logic in your initial for loop
for(let x=0; array.length === outputArray.length; x++)
Your condition array.length === outputArray.length will always initially return false as your output array is a different length to your initial array. This will instantly end the for loop because the condition to continue the iteration has failed.
The for loop is essentially while(contition is true) increment x; Your condition starts false.
to correct it simple change the condition so it is always true until your criteria is met:
for(let x=0; outputArray.length < array.length; x++)
function vowelMatching(vowels) {
return vowels.match(/[aeiou]/ig) || [];
}
function arrangeByVowels(array) {
const outputArray = [];
for(let x=0; outputArray.length < array.length; x++) { // Logic error was here.
const filterArray = [];
for(let i =0; i<array.length; i++) {
if (vowelMatching(array[i]).length === x) {
filterArray.push(array[i])
}
}
outputArray.push(...filterArray.sort())
}
return outputArray
}
console.log(arrangeByVowels(['dog', 'gAg', 'porcupine', 'qq', 'alligator', 'cat', 'elephant', 'albatross', 'hippo', 'goat', 'potato', 'banana','aardvark' ]))

This can be done in a single .sort();
let source = ['dog', 'gAg', 'qq', 'cat', 'elephant', 'hippo', 'goat', 'arose', 'unity', 'arise']
let result = source.sort((a,b) => {
let aVowels = a.match(/[aeiou]/gi) || [];
let bVowels = b.match(/[aeiou]/gi) || [];
return aVowels.length === bVowels.length ?
(a > b ? 1 : -1) : // same number of vowels so compare alphabetically
(aVowels.length > bVowels.length ? 1 : -1) // different number of vowels so compare lengths
})
console.log(result);

Related

Finding first occurrence of each digit in an array

Taking each four digit number of an array in turn, return the number that you are on when all of the digits 0-9 have been discovered. If not all of the digits can be found, return "Missing digits!"
I've tried to loop through then set a conditional if (i != i+1) push into new array this just gave me the array, it's apparent my logic is wrong. could anyone help me out
For example calling this function with
arr = findAllDigits([5175, 4538, 2926, 5057, 6401, 4376, 2280, 6137, 8798, 9083])
the code should return 5057.
While calling
arr = findAllDigits([4883, 3876, 7769, 9846, 9546, 9634, 9696, 2832, 6822, 6868])
should return "missing numbers"
function findAllDigits(arr) {
newArr = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] != arr[i + 1]) newArr.push(arr[i]);
console.log(newArr);
}
}
Do I need to split because it is taking everything before the comma as
one number, then iterate over?
You can use Set here
Loop over the array and then create a set, You have to return the current number if set size becomes 10 because you need to check 0-9
function findAllDigits(arr) {
const set = new Set();
for (let n of arr) {
String(n)
.split("")
.forEach((c) => set.add(c));
if (set.size === 10) return n;
}
return "Missing digits!";
}
const arr1 = [5175, 4538, 2926, 5057, 6401, 4376, 2280, 6137, 8798, 9083];
const arr2 = [4883, 3876, 7769, 9846, 9546, 9634, 9696, 2832, 6822, 6868];
console.log(findAllDigits(arr1));
console.log(findAllDigits(arr2));
Your for loop is only checking to see if the array entry is equal to the next one. You need to split up the digits inside each entry and store them individually:
function findAllDigits(arr) {
newArr = [];
for (let i = 0; i < arr.length; i++) {
// now iterate the individual digits
const entryAsString = arr[i].toString();
for (let j = 0; j < entryAsString.length; j++) {
// if we haven't seen the digit before, add it to the array
if(!newArr.includes(j) {
newArr.push(j);
}
}
// we know we have all digits when newArr is 10 entries long
if (newArr.length) {
console.log(arr[i]);
// you can also return this value here
}
}
}
One more solution:
const arr1 = [5175, 4538, 2926, 5057, 6401, 4376, 2280, 6137, 8798, 9083];
const arr2 = [4883, 3876, 7769, 9846, 9546, 9634, 9696, 2832, 6822, 6868];
const findAllDigits = (arr) => {
// Declare new Set: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
const digits = new Set();
// return the first item from array that fits the condition,
// find() method: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
return arr.find((curr) => (
// String(5175) -> '5175' : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
// [...'5175'] -> ['5','1','7','5'] : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
// .forEach(digits.add, digits) - forEach with callback function and context : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
// comma operator lets get rid of return : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
[...String(curr)].forEach(digits.add, digits),
// condition - is find() method need to return an item
(digits.size === 10)
// if returned value is not undefined or null return finded number oterwise error string
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
)) ?? "Missing digits!";
};
console.log(findAllDigits(arr1)); //5057
console.log(findAllDigits(arr2)); //Missing digits!

Why isn't my for loop incrementing and why isn't my splice being changed?

I came across a problem that seemed very straightforward, but as I coded more some of my expected return were not as I expected.
Any help is appreciated. If you do provide any help please explain your approach and how I got stuck.
The problem:
We're given a string and need to see if it can be broken down into words from a dictionary array. For example:
const str = "applecomputer";
const dictArr = ["apple", "computer"];
stringBreakdown(str, dictArr);
// true
Assuming that there are no repeats in the dictionary array, can you write a method that will return true if the string can be broken down into words from the array, or false if not?
The two test cases:
Expect stringBreakdown('crazyrichasians', [ 'crazy', 'rich', 'asians' ]) // to return true
Expect stringBreakdown('lockcombination', [ 'lock', 'combo' ]) // to return false
My code and approach:
Create a hash map of all the characters in the string
Create a helper function remove charters from each string in the array
As I remove the character in the string I will also decrease each time I've seen that letter from the hash map
If I seen all the letters in that string then I will remove it from the given array
Lastly, if the given array length is less than 0, return true because I was able to make all of the word or turn false because more words are in the array
const stringBreakdown = (str, dictArr)=> {
let hashDictionary = {};
let shouldRemoveWord
for(let x = 0; x <= str.length-1;x++){
!hashDictionary[str[x]] ? hashDictionary[str[x]] =1 : hashDictionary[str[x]]+=1
}
for(let y = 0; y < dictArr.length;y++ ){
shouldRemoveWord = removeLetters(hashDictionary,dictArr[y])
if(shouldRemoveWord === true){
dictArr.splice(y,1)
}
}
console.log('dictArr',dictArr)
return dictArr.length > 0 ? true : false;
}
const removeLetters = (hash,word) =>{
let modifiedWord = word.split('')
for(let k = 0; k < modifiedWord.length;k++){
if(hash[word[k]]){
modifiedWord.splice(k,1)
hash[word[k]]-=1
}
}
return modifiedWord.join('').length < 0 ? true : false;
}
You can go over each word provided in the array then:
Firstly, check if the length of all the words combined together matches with the length of the string under test. If not return false.
Secondly, if the length matches, then check whether every word as a whole is included as a sub-string in the supplied string:
function stringBreakdown(str, dictArr){
return dictArr.join("").length === str.length
&&
dictArr.every(word => str.includes(word));
}
//tests
console.log(stringBreakdown('crazyrichasians', [ 'crazy', 'rich', 'asians' ]));
console.log(stringBreakdown('lockcombination', [ 'lock', 'combo' ]));
console.log(stringBreakdown('applecomputer', [ 'apple', 'computer']));
console.log(stringBreakdown('appelcomputer', [ 'apple', 'computer']));
console.log(stringBreakdown('appcolemputer', [ 'apple', 'computer']));
console.log(stringBreakdown('applecomputer', [ 'app', 'le', 'computer']));
Your approach is dubious as when you go through each character by character you are not looking at the word it forms i.e. in your case if applecomputer is the string and the array has ['appel', 'comterpu'] your algorithm will return true in this case.
It is because you are making a character map from the inout string str and then going through each word's character and decrementing the occurrence of it in the character map so the combination doesn't matter.
const stringBreakdown = (str, dictArr)=> {
let hashDictionary = {};
let shouldRemoveWord
for(let x = 0; x <= str.length-1;x++){
!hashDictionary[str[x]] ? hashDictionary[str[x]] =1 : hashDictionary[str[x]]+=1
}
for(let y = 0; y < dictArr.length;y++ ){
shouldRemoveWord = removeLetters(hashDictionary,dictArr[y])
if(shouldRemoveWord === true){
dictArr.splice(y,1)
}
}
return dictArr.length > 0 ? true : false;
}
const removeLetters = (hash,word) =>{
let modifiedWord = word.split('')
for(let k = 0; k < modifiedWord.length;k++){
if(hash[word[k]]){
modifiedWord.splice(k,1)
hash[word[k]]-=1
}
}
return modifiedWord.join('').length < 0 ? true : false;
}
//doesn't work outputs true
console.log(stringBreakdown('applecomputer', ['appel', 'computer']));
simply loop through the dictionary
const dictArr = ["lock", "combo"];
function checkInDic(val){
var len = 0;
dictArr.forEach(element => {
if(val.includes(element)){
len += element.length;
}else{
return false;
}
});
if(len == val.length){
return true;
}else{
return false;
}
}

Remove Strings, Keep Numbers In Array With JavaScript

Other articles talk about removing strings from an array based on a search term.
But I'm trying to indentify which elements are strings and which elements are numbers in an array, and then remove all strings to return a new array.
function filter_list(l) {
let newArray = [];
for (let i = 0; i < l.length; i ++) {
if (i !== "^[a-zA-Z0-9_.-]*$") {
newArray = newArray + i;
}
}
return newArray;
}
This is returning 0123.
Why is it not returning an array?
Why is if (i !== "^[a-zA-Z0-9_.-]*$") not working? How else can I check for when an element is a string (something in quotes) within the array?
https://www.codewars.com/kata/list-filtering/train/javascript
Thanks
You can is typeof keyword. and filter(). I have tested the code its passing all tests in codewars.
Using ES6 Arrow Function
function filter_list(l) {
return l.filter(x => typeof x === "number");
}
console.log(filter_list([1,2,'a','b']))
Without Arrow Function
function filter_list(l) {
return l.filter(function(x){
return typeof x === "number"
});
}
console.log(filter_list([1,2,'a','b']))
Using Simple Loops
function filter_list(l) {
let newArr = [];
for(let i = 0;i<l.length;i++){
if(typeof l[i] === "number") newArr.push(l[i]);
}
return newArr
}
console.log(filter_list([1,2,'a','b']))
Regex is not good way to parse such table. Try isNaN
console.log(
[1,2,3,4,5, 'a', 'b', 1, 3].filter(item => !isNaN(item) ? item : '')
)
If you want less hacky way try
function filter_list(l) {
// l is very bad name, because look similar to i
let newArray = [];
for (let i = 0; i < l.length; i ++) {
!isNaN(l[i]) ? newArray.push(l[i]) : ''
}
return newArray;
}
or even
for (let i = 0; i < l.length; i ++) {
!isNaN(l[i]) ? newArray[i] = l[i] : ''
}
Hovewer, this task can be done with regexes, but I cannot recommend this solution.
[1,2,3,4,5, 'a', 'b', 1, 3].join(' ').replace(/\D/gm, '').split('')
var numberArray: any[];
numberArray.filter(Number)
Using this you can filter only numbers in an array and then can performe what you want.
function filter_list(l) {
return l.filter(x => typeof x === "number");
}
console.log(filter_list([1,2,'a','b']))
I worked out a simple answer that will work as well using the same logic required to solve your problem. I used it on an example where you have an array of temperature values, and you want to remove all the values which are strings from the existing array, then populate the new empty array.You can use typeof operator to identify the type of value in the temperatures array at position i which is the index of that array element. If the type of that value is not a string then push the value of the temperatures array at the current index position to the new array.
const temperatures = [3, -2, -6, -1, 'error', 9, 13, 17, 15, 14, 9, 5];
const cleanTemperatures = [];
for (let i = 0; i < temperatures.length; i++) {
if (typeof temperatures[i] !== 'string') {
cleanTemperatures.push(temperatures[i]);
}
}

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

Counting elements in an array and adding them into an object [duplicate]

This question already has answers here:
Count unique elements in array without sorting
(9 answers)
Closed 6 years ago.
Okay I researched this one and couldn't find an answer here so hopefully this isn't a duplicate. I'm also trying not to be specific in order to figure this out on my own.
var arr = ['cat','car','cat','dog','car','dog']
function orgNums(input) {
var count = 0;
var obj = {};
for (var i = 0; i < input.length; i++) {
if (input[i] === 'cat')
obj['cat'] = count++;
}
return obj;
}
I want it to return {cat:2} but I'm getting {cat:1}
Eventually I want it to return {cat:2, car:1, dog:2, gerbil:1}
So I tried using obj[cat] = ++count and I'm getting the answer I want, but when I try a second if statement: if input[i] === 'dog', obj[dog] = ++countI get {cat:2, dog:4}. I noticed that it's taking what count already is at, 0, then moves it to 2 to count cat, then moves it to 4, taking the dog count of 2 and adding 2 to it. How do I prevent that from happening so that count restarts at 0 each time?
EDIT:
So this works beautifully
var arr = ['cat', 'car', 'cat', 'dog', 'car', 'dog']
function orgNums(input) {
var obj = {};
for (var i = 0; i < input.length; i++) {
obj[input[i]] = obj[input[i]] || 0;
obj[input[i]]++;
}
return obj;
}
console.log(orgNums(arr));
but the final output i actually want is:
[
{cat:1
dog:2
}
{car:2
}
]
So I tried throwing in an if statement like this:
if (input[i] === 'cat'||'dog')
but it's still throwing car into the object. I'll try to figure out the multiple objects in the array. Thanks again!
You could just reduce the array, and count the keys as you go
var arr = ['cat', 'car', 'cat', 'dog', 'car', 'dog', 'horse'];
function orgNums(input) {
return input.reduce((a, b) => {
return b in a ? a[b]++ : a[b] = 1, a;
}, {});
}
console.log(orgNums(arr));
The assignment of count, which is used in the loop, does not work in this case, because of the postfix increment count++. This gives you the value of the counter and increment later, If you take the prefix increment ++count, you get the incremented value assigned.
// Postfix
var x = 3;
y = x++; // y = 3, x = 4
// Prefix
var a = 2;
b = ++a; // a = 3, b = 3
But you can omit the variable and count directly with a property of the object. for all items in one loop.
You could use the element as key for the object. Then assign with zero, if not exist and increment.
var arr = ['cat', 'car', 'cat', 'dog', 'car', 'dog']
function orgNums(input) {
var obj = {};
for (var i = 0; i < input.length; i++) {
obj[input[i]] = obj[input[i]] || 0;
obj[input[i]]++;
}
return obj;
}
console.log(orgNums(arr));
A more compact version of the above with Array#forEach
var arr = ['cat', 'car', 'cat', 'dog', 'car', 'dog']
function orgNums(input) {
var obj = {};
input.forEach(function (item) {
obj[item] = (obj[item] || 0) + 1;
});
return obj;
}
console.log(orgNums(arr));

Categories

Resources