Mini Linter - Step 4

I just completed the Mini Linter project. For step #4, I couldn’t figure out how to get what I wanted and had to watch the walkthrough video. Unfortunately, the walkthrough video does not complete the step using the iterator methods we learned, which seems the whole point of this exercise.

Here’s how I completed Step 4 after watching the video:

for (word of betterWords) {
  if (word === 'really') {
  reallyCount += 1;
  } else if (word === 'very') {
    veryCount += 1;
  } else if (word === 'basically') {
    basicallyCount += 1;
  }
}

It seems like if we were creating a real linter, we would automate it by looping over every word in the overusedWords array and compare those words to the betterWords array, rather than declaring each word in a for-loop like the example video shows.

The problem is, I can’t figure out a how to get the methods to do what I want. So far, I can get an array that includes all the instances of overused words in the text:

const overusedCount = betterWords.filter(word => {
  for (i= 0; i < overusedWords.length; i++) {
  if (overusedWords[i] === word) {
    return true;
  } 
  }
}
)

console.log(overusedCount);  // returns [ 'really',
  'basically',
  'really',
  'very',
  'very',
  'very',
  'very',
  'very' ]

But I can’t figure out how to count all those words in a way that’s automated – i.e., in a way that doesn’t require me to look at each word and write a separate console.log statement for it. I want to generate the list of overused words automatically based on which words are in the list. Any ideas how I might do that?

This brings about a question about the use of distinct variables for each over-used word. Your idea of a list seems a suitable alternative to loosely and arbitrarily defined variables. If we have a list of the words we can report and count them at that stage. So all that’s needed is data collection followed by analysis rather than doing the analysis concurrently.

Now consider the .filter() method. Does it need a contained loop? It is an iterator, after all. Not to mention the callback is supposed to be a predicate function. A loop is not a predicate. Our function will simply ask the question in your loop as a logical expression, not an if statement.

Eg.

a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

console.log(a.filter(x => x % 2 == 0))

I think what you’re saying is that the for loop I built into the .filter method is superfluous since the .filter method is an iterator to begin with.

The reason I did that was that I couldn’t figure out how to get the .filter method to behave like I wanted to, and generate an array based on overlap between overusedWords and betterWords. I’m going to come back to this at some point this week and see if I can figure it out – hopefully some time away will help me solve it.

1 Like

Think only in terms of giving the .filter() a predicate callback, not a whole bunch of other logic. It needs to return true or false for everything the iterator is iterating over, and nothing else. When the return from the predicate is true, the value that was just tested gets appended to the return array.

2 Likes

Did some more tweaking, and I came up with a function that works. I still used a loop (for…of), but I removed the complicated logic from the .filter() method and am satisfied with the result.

const overusedList = () => {
  for (let i of overusedWords) {
    const overusedArray = betterWords.filter(word => word === i);
      console.log(`You used the word "${i}" ${overusedArray.length} times.`)
    };
}

So now, if in my overusedWords array I have:

let overusedWords = ['really', 'very', 'basically', 'foo', 'end'];

I get the output:
You used the word “really” 2 times.
You used the word “very” 5 times.
You used the word “basically” 1 times.
You used the word “foo” 0 times.
You used the word “end” 1 times.

…which is superior to having to manually declare a counter for each word in the overusedWords array.

Thanks for your comments on this one–if you can think of a better or simplified way of doing that, I’d love to hear it.

arr = betterWords.filter(word => overusedWords.includes(word))

Will need a link to the exercise so I can put this in context and test it.

Mini-linter link:

https://www.codecademy.com/courses/introduction-to-javascript/projects/mini-linter

Here’s the full code I used:

let story = 'Last weekend, I took literally the most beautiful bike ride of my life. The route is called "The 9W to Nyack" and it actually stretches all the way from Riverside Park in Manhattan to South Nyack, New Jersey. It\'s really an adventure from beginning to end! It is a 48 mile loop and it basically took me an entire day. I stopped at Riverbank State Park to take some extremely artsy photos. It was a short stop, though, because I had a really long way left to go. After a quick photo op at the very popular Little Red Lighthouse, I began my trek across the George Washington Bridge into New Jersey.  The GW is actually very long - 4,760 feet! I was already very tired by the time I got to the other side.  An hour later, I reached Greenbrook Nature Sanctuary, an extremely beautiful park along the coast of the Hudson.  Something that was very surprising to me was that near the end of the route you actually cross back into New York! At this point, you are very close to the end.';

let overusedWords = ['really', 'very', 'basically', 'foo', 'end'];

let unnecessaryWords = ['extremely', 'literally', 'actually' ];

// uses the .split() method to split the text into individual words based on spaces

let storyWords = story.split(' ');


// uses the .filter method to filter create an array. As long as each word from storyWords is not included in the unnecessaryWords array, it gets filtered to a new array called betterWords
const betterWords = storyWords.filter(word => !unnecessaryWords.includes(word));

// declaring counter for the loop below

let sentenceCount = 0;

// I got this from the walkthrough...there's also a way to do this using the iterators we learned. The (___ of ___) is a handy loop syntax for arrays and other iterables.

for (let word of betterWords) {
  if (word.includes('.') || word.includes('!')) {
    sentenceCount += 1;
  }
}

/* Doing the above with the .filter() iterator method:

let sentenceFilter = betterWords.filter(word => word.includes('.') || word.includes('!') || word.includes('?'));
console.log(sentenceFilter.length);

*/

/*
Defining a function that will compare betterWords to overusedWords using the filter method, and create a new array when two words match up. It then logs the word using the index of the overusedWords array and the amount of times the word was used using the length of the overusedArray.
*/


const overusedList = () => {
  for (let i of overusedWords) {
    const overusedArray = betterWords.filter(word => word === i);
      console.log(`You used the word "${i}" ${overusedArray.length} times.`)
    };
}


const totalLog = () => {
console.log (`Word count: ${storyWords.length}`);
console.log (`Sentence count: ${sentenceCount}`);
overusedList()
}

totalLog();





// uses the .join() method to put betterWord back together again based on word breaks
//console.log(betterWords.join(' '));


/*

Here's the old code for the overusedList and sentence count functionality...as you can see, it doesn't allow for automation. You have to enter each overused word in manually. Using the .filter() method above is much more useful

// declaring counters for the loop below

let reallyCount = 0;
let veryCount = 0
let basicallyCount = 0;
let sentenceCount = 0;

// I got this from the walkthrough...it seems like there must be a better way using the iterators we learned, but I can't figure out one that works. I also don't quite understand this syntax (___ of ___)

for (word of betterWords) {
  if (word === 'really') {
  reallyCount += 1;
  } else if (word === 'very') {
    veryCount += 1;
  } else if (word === 'basically') {
    basicallyCount += 1;
  } else if (word.includes('.') || word.includes('!')) {
    sentenceCount += 1;
  }
}

*/

This code appears to do what yours does, in a sense…

arr = betterWords.filter(word => overusedWords.includes(word))
console.log(arr)
hist = {}
arr.forEach(x => hist[x] = (hist[x] || 0) + 1)
console.log(hist)

Output

[ 'really',
  'basically',
  'really',
  'very',
  'very',
  'very',
  'very',
  'very' ]
{ really: 2, basically: 1, very: 5 }
1 Like

Nice–I didn’t think to use includes method. Also, I didn’t know the OR assignment operator could be used like that

1 Like

Are you able to reason how that expression works?

1 Like

…not really. I still have problems understanding documentation. I know if I change the 0 to a 1 in your code, all the numbers that get returned increase by 1, which is not what I expect. What about the statement is causing the number values to increment, and what’s causing the keys and values to be separated by a colon in the log?

1 Like
hist[x] = ...

x is the key, and the assignment is the value given to that key. This is standard Object syntax.

obj = {}
obj['foo'] = 'bar'
console.log(obj)    //  { foo: 'bar' }
hist[x] || 0

will yield hist[x] if it exists, else it will yield 0. If the key does not exist it gets inserted with an initial value of 0. In both cases we add 1 to either give a value of 1, or to increment the existing value.

Thank you, this makes sense now–I guess I should have been looking at the object syntax rather than the documentation for the .forEach() method. I’m sure I’ll get better at this as I do it more, but seeing explanations like this is helping me move along faster. In the next few days I’m going to try your code in my project and see if I can get it to work as an alternative.

1 Like

The thing to remember is that your code is better from a learning perspective. One shouldn’t be discarding our code in favor of someone else’s until such time as we could write that code, ourselves. In other words, keep to what you know and keep writing your own code, and getting it to work. Over time and with regular review the refinements and different logic will emerge. Stay your course.

3 Likes