Mini Linter

Something’s not working about my code.

I’m trying to create an array of unique words (uniqueWords) out of given array (storyWords). My idea was to iterate forEach() over the original array, passing a function that would compare the two members of the array using includes(). However, it’s not working and I can’t figure it out.

let uniqueWords = [];
storyWords.forEach(      //storyWords is the given array
  function findUnique(word) {
    if (!uniqueWords.includes(word)) {   //is the array member in question in uniqueWords already?
      uniqueWords.push(word);    //if not, push it
    }
  }
  );

when I console.log uniqueWords, it’s empty.

First question, is it an array of unique words that you want, or an array of all the unduplicated words in the storyWords array?

More to the point of your problem…

forEach takes an anonymous function as a callback. Your function is not anonymous and never gets called. Remove the name and it should work as expected.

Or, move the function to the outside world as a standalone, then reference it in the argument.

Array.forEach(findUnique)
2 Likes

Okay, thanks that worked.

I tried it using all the shorthand/anonymous ways but I think I had forgotten to put a semi-colon at the end of the anonymous function. The shorthand syntax is still pretty new for me.

1 Like

I’m particularly fond of the reference approach since it is so straight forward, and it lets us utilize the function it references in many other ways as it is available to the whole program, not just this one forEach. You could even feasibly have several forEach() calls on different arrays that can use the same callback.

The shorthand, or more correctly, concise arrow function syntax is useful when all we want is a simple callback. For more complicated functions, stick to the traditional form.

Above we have an empty list initialized. That makes it awkward to use the standalone since it names the array, explicitly. It will take some reading but forEach permits us to supply that array as one of its arguments. Until such implementation is in place, we might be better to write the callback in the argument.

All kinds of options, and as fledgling programmers, sometimes difficult to choose which one to use. The beauty of programming is we are used to failure since we try so many things we are almost certain won’t work or that we are told to avoid, but we try them anyway.

When you have time, this might be one of those things that is worth exploring for deeper insight and to expand our usage potential of what may prove to be a very powerful feature of the language. Keep us abreast of your exploration so we can play along.

2 Likes

Follow-up

This appears to do what we expect, but will need more testing…

Text used

storyWords = 'Once upon a time there was a huge dragon that lived at the top of very tall mountain, so tall its peak was mostly hidden by clouds.'.split(' ')
function findUnique(word) {
  if (!this.includes(word)) {
    this.push(word);
  }
}
let uniqueWords = []
storyWords.forEach(findUnique, uniqueWords);
console.log(uniqueWords)

Output

[ 'Once',
  'upon',
  'a',
  'time',
  'there',
  'was',
  'huge',
  'dragon',
  'that',
  'lived',
  'at',
  'the',
  'top',
  'of',
  'very',
  'tall',
  'mountain,',
  'so',
  'its',
  'peak',
  'mostly',
  'hidden',
  'by',
  'clouds.' ]
1 Like

You use the this.x terminology when you’re passing the array in as the third parameter?

As the second argument, yes. That way we don’t have any fixed reference written in the function. It will always (unless we change it) apply to the array we include in the argument. This way the function can be referenced as we described above, by any forEach() regardless of which array it is called on, or passes in.

The only drawback is that our standalone function, as useful as it is as a callback, will not work so well on its own, since this in that sense would be the window object.

A work around for that would be to define a function that not only contains that function, but also contains the forEach.

const findUniqueWords = function(array) {
  function findUnique(word) {
    if (!this.includes(word)) {
      this.push(word)
    }
  }
  const uniques = []
  array.forEach(findUnique, uniques)
  return uniques
}
let uniqueWords = findUniqueWords(storyWords)
console.log(uniqueWords)

Off to test this theory…

Tested and working as expected.

1 Like

Correction… It would be the third argument IF we include an INDEX parameter. And above, word is akin to a block parameter, so don’t mind my mixing up the words. The key is we are learning what each of them represent.

FTR, the exercise is located here…

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

Yes, I was assuming the index parameter was in there. All of this is very new to me since I just read the docs and it’s a little beyond my level right now.

In the spec, anything with a [, in front of it is optional, but the order matters. The example above takes the methodology a bit beyond what this course expects so don’t feel let down if it is hard to grasp. Start from the ground up, along the same lines as you have to this point, and come back to this topic when you’re feeling more confident and have grown your wings. Ping this topic if you ever wish to revive it.

1 Like

In subtask 4 we are asked to count occurrences of “overusedWords”, not occurrence of every specific overused word, so I propose simpler solution than video has provided, check this out and tell me what you think about it:

const howManyOverusedWords = storyWords.filter(word => {
  if(overusedWords.includes(word)) {
     return word;
  };
});

console.log(howManyOverusedWords.length);

It looks viable to me, although I haven’t tested it (also, I never have watched those videos or walkthroughs).

The only thing is I was looking to push myself and that’s why I was working on the “extra credit” assignments. The task of identifying the words used most, and their frequencies, is a challenge. Right at the edge of my abilities… which is a good place to be.

Consider,

const howManyOverusedWords = storyWords.filter(word => overusedWords.includes(word)).length;

filter takes a predicate function, so it is already a form of if, especially when we consider the .includes() returns a boolean.

 > a = [1,2,3,4,5,6,7,8,9]
<- (9) [1, 2, 3, 4, 5, 6, 7, 8, 9]
 > b = [2,4,6]
<- (3) [2, 4, 6]
 > a.filter(x => b.includes(x)).length
<- 3