Whale talk : loops

let input = ‘we dont talk anymore’;
input.toLowerCase();
const vowels = [‘a’, ‘e’, ‘i’, ‘o’, ‘u’];

let resultArray = ;

for (let i = 0; i < input.length; i++){
for (let j = 0; j < vowels.length; j++){
if (input[i] === vowels[j]){
resultArray.push(input[i]);
};
if (input [i] === ‘e’ || input[i] === ‘u’){
resultArray.push(input[i])
resultArray.push(input[i])
break;
};
}
};
console.log(resultArray.join(’’).toUpperCase());

this is my code, and it’s only way i can make it work. Can it get more efficient.

Yes, this can become more efficient. You can use methods like forEach() (This can be learned in the iterators lessons.

1 Like

Perhaps it can, but we wouldn’t know that yet, and it will a long time before any benchmarking will be needed. Focus on syntax, keywords, concepts and constructs and forget about efficiency.

There is an unwritten rule with regards to learning programming…

Don't try to be perfect; jump in and play with the code.
1 Like

I am confused why you would call the push twice? Considering you already pushed these vowels in the previous if statement:

if (input[i] === vowels[j]){
   resultArray.push(input[i]);
};

If you run the code, it works. It’s a little bit of a fluke maybe. Since ‘a’ is the first element in the vowels array, when an ‘e’ or ‘u’ is up for comparison, it fails the first if (not equal to ‘a’), and then gets pushed twice in the next if block, after which break kicks it out to the outer for loop. A bit unorthodox, perhaps, but it works :slightly_smiling_face:

1 Like

I ran it a couple of times, it works, but still don’t quite get it.

if (input[i] === vowels[j]){
        resultArray.push(input[i]);
}

The above code should push all vowels one time.

If this is followed by:

if (input [i] === ‘e’ || input[i] === ‘u’) {
     resultArray.push(input[i]);
     resultArray.push(input[i]);
     break;
}

I would expect it to push e or u another two times, resulting in 3 e’s or 3 u’s in the result.

However when I remove one of the pushes from the second if statement, I get the same result as when I remove that if statement entirely. This confuses me big time :sweat_smile:

I personally used a nested if statement.

for (let i = 0; i < input.length; i++) {
    	for (let y = 0; y < vowels.length; y++) {
         	if (input[i] === vowels[y]) {
               	    resultArray.push(input[i]);
            	    if (input[i] === 'e' || input[i] === 'u') {
               	       resultArray.push(input[i]);
      	            }
    	        }
         }
}

Can you please post a link to the exercise page? Thanks.

https://www.codecademy.com/paths/web-development/tracks/web-dev-js-arrays-loops-objects/modules/learn-javascript-loops/projects/whale-talk

1 Like

I think what happens is that because ‘e’ nor ‘u’ are the first letter in the vowel array the first if statement is false but the second one is true popping the letter twice then the break; exits out of the inner for loop going to the next letter in the input.

1 Like

for loops are an explicit form of iteration, but not the only means we have. There are iterators that are well suited to this problem…

filter    =>  an Array method
forEach   =>  an Array method
includes  =>  String or Array method

Even split is a sort of iterator exclusive to String objects.

1 Like

If the order of the vowels were changed, in the array, so that ‘e’ or ‘u’ were first, then whichever of them was first would appear 3 times in the result.

I’ve gone back, and revisited this one a few times. My initial solution looked pretty close to what you have. Most recently I came up with this:

let input = 'To be, or not to be. That is the question.'
console.log(input.toUpperCase().split('').filter(
  l => ['A', 'E', 'I', 'O', 'U'].includes(l)).map(
  l => l == 'E' || l == 'U' ? l.repeat(2) : l).join(''))

Oh yeah! Forgot to mention map as an iterator, as opposed to forEach, and repeat completely never crossed my mind. Nice one!

Recall that above we noted that includes is both String and Array.

let input = 'To be, or not to be. That is the question.'
console.log(input.toUpperCase().split('').filter(
  l => 'AEIOU'.includes(l)).map(
  l => l == 'E' || l == 'U' ? l.repeat(2) : l).join(''))

Aside

Even while the variables are in different scopes, I still prefer to use different names. l is probably the least preferred name of any variable since it looks so much like a numeral, 1.

1 Like

D’oh. I hadn’t considered that, so this is better:

let input = 'To be, or not to be. That is the question.';
console.log(input.toUpperCase().split('').filter(
  l => 'AEIOU'.includes(l)).map(
  l => l == 'E' || l == 'U' ? l.repeat(2) : l).join(''))

Thanks!

1 Like
let input = 'To be, or not to be. That is the question.'
 > console.log(
     input.toUpperCase().split('').filter(
       u => 'AEIOU'.includes(u)
     ).map(
       v => 'EU'.includes(v) ? v.repeat(2) : v
     ).join('')
   )
   OEEOOOEEAIEEUUEEIO
<- undefined
1 Like

A little less elegant, perhaps, but more efficient possibly?

const input = 'To be, or not to be. That is the question.';

let result = ''
for(let x of input.toUpperCase()){
  result += 'AEIOU'.includes(x) ? 'EU'.includes(x) ? x.repeat(2) : x : ''
}
console.log(result)

Avoids converting string to array and back.

We could say there is an explicit value assigned at each iteration of the input; that much is certain. It would take a Big O analysis to determine if there are nested loops, or not. The earlier example is a 2N^2 model, so in Big O, O(n^2). All this is conjecture until someone actually analyzes it.

1 Like

let input = ‘hi how are you’;
input.toLowerCase();
const vowels = [‘a’, ‘e’, ‘i’, ‘o’, ‘u’];

let resultArray = ;

for (let i = 0; i < input.length; i++){
for (let j = 0; j < vowels.length; j++){
if (input [i] === ‘e’ || input[i] === ‘u’ ){
resultArray.push(input[i]);
resultArray.push(input[i]);
break;
} else if (input[i] === vowels[j]) {
resultArray.push(input[i]);
break;
};
};
};
console.log(resultArray.join(’’).toUpperCase());

I think i found the solution with no bugs.