Mini Linter Qs 3 - different methods to achieve the same answer

Hi there - I’m chugging through the Full Stack course and loving it!

I’ve got to the Mini Linter project and am trying to get some alternative answers for question 3…

It asks

Blockquote
There is an array of words that are unnecessary. Iterate over your array to filter out these words. Save the remaining words in an array called betterWords . There are several ways that you could achieve this.

I’m curious, as I initially thought that I could create an empty array and then iterate through the unnecessary word array, then compare these to the betterWords string and then only ‘push’ the non-matching words to the new betterWords array. This was my idea, but I can’t get it to work:

let betterWords = [];

for (let i = 0; i < unnecessaryWords.length; i++) {
    for (let j = 0; j < storyWords.length; j++) {
        if (unnecessaryWords[i] != storyWords[j]) {
            betterWords.push(storyWords[j];
        }
    }
}

This is my first post, I can’t seem to preview it so will edit it once published (if I can) if there are formatting errors.

TIA

Can you think of two iterators that when combined as a higher-order function will return an array?

No need to edit it* – it’s all there: The link to the project and correct formatting of code. :slightly_smiling_face:

*Apart from the tiny syntax error in your push method. Once that’s corrected, you probably get your code to work because I can’t see any logic errors in it.
And then you can look for more elegant alternatives as proposed by @mtf and question 3.

Wow - superfast answers, thanks both!

@mirja_t I’ve corrected that slight syntax error, but still getting the same result - which is an unaltered storyWords string (the unnecessaryWords have not been removed)

@mtf thanks - I’ll check the MDN documentation, I suspect I should know the answer… but I don’t!

Which iterator(s) return an array? Which iterators are conditional?

OK, here goes!

for ... in and for ... of look like they will both return an array

for, do ... while and while look to me to be conditional

No, push doesn’t remove the words from the original array. You now have a copy with only the words you want to keep in the array betterWords, right? That’s how it should be the way you did it. If you wanted to remove the items in the original array, you’d have to use a method like splice for example.

I don’t know if you are a native English speaker and I’m not sure if that has something to do with it: But it took me a while to realize that not only for loops, but also methods like map() or filter() etc. are referred to as iterators…

None of those returns an array. Have you practiced with any of the iterators, yet?

I’m at the end of the iterators course… back to the start, then! I will pay particular attention to what each iterator returns, as that appears one of the key things to learn.

@mirja_t I expected/wanted the first loop to check the first word from unnecessaryWords against every individual word from the storyWords array. If they were not the same, I wanted to push that storyWord into the betterWords array. But the output is exactly the same as the input.

Yep. I was too focused on the syntax error. I see the logical error now. Probably your betterWords array is even bigger than the original array? Did you log betterWords.length and does it log the same as storyWords?
You are iterating through each word of storyWords as many times as there are words in the unnecessaryWords array. And even if a word from the unnecessaryWords array is in the betterWords array, you’re pushing it anyway. Let’s say these are your unnecessary words: [‘anyway’, ‘somewhat’, ‘whatever’]
Then let’s take this case: The first word in storyWords is ‘whatever’. Then this happens:
unnecessaryWords[0] != storyWords[0] (whatever != anyway)
It gets pushed to the array.
So you need to look for a method that iterates through the array, takes a condition, and returns the array.

Edit: the hint of step 3 mentions one of these methods.

Thanks - I’ve completed the exercise using the .filter() method successfully.

It is just the nerd part of me… I was looking for other ways to do it, and have been trying to get alternative options to work. In one of the earlier exercises we used two nested for... loops and I thought I could do something similar here.

In direct answer to your question - the .length of the storyWords and the betterWords are exactly the same. My .push() function is directing all the words from the original array into the new array without appearing to go through my inner loop?

Usually iterators like filter() and includes() are the cleaner and more concise methods compared to for loops. When you can avoid loops as in this case, you better go for the combination of filter() and includes() as suggested in the exercise. That way, you go with a single line of code.

I am a bit surprised that both arrays are the same length, so I ran your code

let storyWords = ['WTF', 'I', 'really', 'don\'t', 'get', 'that'];
let unnecessaryWords = ['WTF', 'really'];

let betterWords = [];

for (let i = 0; i < unnecessaryWords.length; i++) {
    for (let j = 0; j < storyWords.length; j++) {
        if (unnecessaryWords[i] != storyWords[j]) {
            betterWords.push(storyWords[j]);
        }
    }
}

console.log(betterWords.join(' ')); //"I really don't get that WTF I don't get that"
console.log(betterWords.length, storyWords.length); // 10, 6

Ok - I’m starting to appreciate how my code is producing the result. By running the two loops, I am actually pushing the same word twice when the

unnecessaryWords[i] != storyWords[j]

comparison is true.

1 Like

Just for completeness - if anyone else finds this thread and was stuck…

I foolishly assumed ‘iterators’ and ‘loops’ were synonymous, and didn’t realise that methods such as:
.forEach(), .reduce(), .map(), .filter() and others were also considered iterators.

For anyone following the Jon Duckett “Javascript and JQuery” book, he refers to them as array methods and has a section on pages 530-537 that I found worth looking at for this exercise.

My misunderstanding - thanks to @mtf and @mirja_t for your help. I have redone the whole Objects lessons and paid much better attention to the output of these methods.

To answer your question @mtf : .map() and .filter() both return arrays :wink:

1 Like

Only difference being…?

Great question - my understanding is:

.map() will return a new array, having performed a function, with the same number of elements in the initial array (ie there will always be the same .length in both arrays)

.filter() will return a new array, but with only those elements which have passed the test set by the calling function (ie there are likely to be fewer elements in the returned array when it is used to filter/screen)

1 Like

So now how would we refactor your nested loop?

1 Like

Well this is what I used in the lesson:

let filteredWords = storyWords.filter(word => { return !unnecessaryWords.includes(word); });

So - .filter() was used to return a new array containing ONLY the elements that passed the middle line (ie they did not match the words in the unnecessaryWords array)

But the lesson hints there are other ways to achieve this…

1 Like

Which is the reason we poke the rabbit to see where it leads…

const filteredWorlds = storyWords.filter(word => ! unnecessaryWords.includes(word));

making full use of implicit return.

This project could be played with for a good long while with the aim to modularize it. Start with the text as the function’s input and build in helper functions.

const miniLinter = (storyWords, unnecessaryWords) => {
  const nix = word => ! unnecessaryWords.includes(word);
  const filteredWorlds = storyWords.filter(nix);
  // ...
}

Extra Study

Q. Why don’t iterators support break?

A. Because they are iterators.

Think of a bead of water at the top of a rope. It will travel all the way to bottom and drop to the ground eventually, covering every inch of that rope. That’s what iterators do. They start at the beginning and don’t stop until they reach an End Of File, or some such signal. They work on a simple principle,

this.one
next.one
on fail stop iteration

this.one becomes next.one until the end is reached. We step through the links in a data chain, end to end. There is no breaking condition.


for (let i =0; i < 10; i++) {} and for (let x of array) {} are both iterators. They’re just not higher order functions. There is no abstraction, and they support break.