Understanding how this code works

https://www.codecademy.com/paths/full-stack-engineer-career-path/tracks/fscp-javascript-syntax-part-ii/modules/fecp-learn-javascript-syntax-iterators/projects/mini-linter [you do not need this]

My Mini Linter is done and runs fine. Unlike the solution, my program was not dependent on on the content of overusedWords. I found code similar to my needs in getting counts of overusedWords and adapted it. If only I clearly understood it. This code works.

Note the ternary operator. The condition part is not a logical statement but an assignment statement. That is confusing. What I understand of this, is that when a new word is found, the word is added to the counts object as a property with the value of 1. When a previous word is encountered, the value is incremented by 1. Thus we have a result like this: counts = { really: 2, basically: 1, very: 5 } . I can not explain the mechanics of the code.

//foundOverusedWords is the result of iterating over betterWords and finding the indexes where overused words occur
//then using those indexes to build foundOverusedWords
foundOverusedWords = ['really', 'basically', 'really', 'very', 'very', 'very', 'very', 'very'];

const counts = {};
for (const word of foundOverusedWords) {
  counts[word] = counts[word] ? counts[word] + 1 : 1; 
}
//output counts = { really: 2, basically: 1, very: 5 }  this is correct

console.log(counts);

Rule one of learning to program is to not use someone else’s code, especially if you do not understand it and could not write it yourself. This can come back to bite you.

The ternary statement above consists of an assignment to the property at left of the expression evaluation at right. The evaluation is of the truth value of the operand before the ? determines which expression is assigned. The one before the colon is assigned if truthy, else the other one is assigned.

What you have there is the makings of a frequency table, as you can see by the result. My challenge for you now is to write a version using if..else that produces the same thing.

I have done that. So the assignment does produce a “ truthy” value. Now walking dog in 20 degree weather. More later.

Not produce, evaluate.

value/expression ? is truthy : is falsy

We are not concerned with the returned values, only the one in the conditional operand.

I’ve read this a couple of times in the forum now and since I honestly don’t understand why that is considered bad, let me ask you, how this comes back to bite and who defined that rule?
My experience is that looking at someone else’s code widened my perspective. Since you can do so much with for loops and if/else logic only, I think I would have been stuck at that point for ages if I wouldn’t have peeked at how other people solve things, to write code more elegantly. At the beginning, I copied a lot – at first I didn’t understand it, but I had to in order to adjust it, so understanding came later. And I think that hasn’t bit me yet. So why do you consider it bad to copy other people’s (public) code and try to understand it? Don’t get me wrong: I’m seriously interested.

Nobody said it was ‘bad.’ It is inappropriate to use copied code on a lesson exercise. We prove nothing of what was just learned from the narrative. OWN SOLUTIONS is what they should be submitting, not copied code. That makes them cheaters. They deserve to fall on their face.

When in the process of review and extra study/practice that would be the appropriate time to examine other code models, not to copy them, but to learn from them. That’s not cheating, but recommended. I still stand by my view that if you cannot write it yourself, don’t use it.

1 Like

Oh, ok, that sounds reasonable. If copied code is used in a lesson to get the check, I would also consider that a waste of opportunity.
“Rule one of learning to program is to not use someone else’s code” just sounded very general to me and opposed my personal experience. But in regard to the lessons, I would agree.

1 Like

Bare in mind that copied code could well contain errors which we mightn’t spot or even know how to correct. If we write something, we should/will be better equipped to debug and solve issues, which is also a teaching moment.

1 Like

This code was not copied. It existed in another form, solving an entirely different problem. I adapted it to my situation. And I will not quit until I completely understand it. I have learned a lot from seeing others’ codes and then duplicating and stepping thru them to see how everything works. In this instance, the debugger could not step thru the ternary operator. I surmised from the assignment that the condition part was evaluating true. Later, I learned from MDN documentation on the ternary operator that the proper terminology is truthy and falsy. Then I had to find the difference between truth and falsy, and that javascript uses type conversion on boolean context. I did all this before posting my question, and I would not have learned all this had I not used this code. Rule one for me is never paint anything as black or white.

Then I fail to understand why this was a question in the first place. At any length, hopefully now you have your answer.

I do not completely understand how the ternary condition accomplishes adding the counts[word] word property with the value of 1. All I see is the property is added without a value. counts[word] = counts[word] is still an enigma. This code is easy to understand.

 
let foundOverUsedWords = ['really', 'basically', 'really', 'very', 'very', 'very', 'very', 'very'];

let counts = {};
for (const word of foundOverUsedWords) {
 // counts[word] = counts[word] ? counts[word] + 1 : 1; //the else part is never executed, so this is dummy else
  if (counts[word] == undefined) {
    counts[word] = 1;
  } else {
    counts[word] = counts[word] + 1;
  }
}
console.log(counts); //output counts = { really: 2, basically: 1, very: 5 }

The assignment is the key. word is polled from the array, and used as a subscript of counts to bind with that property, if it exists, or to create the property if it doesn’t. When first created it is given the initial value of 1, meaning that one instance of the word has been encountered. If it does exist then 1 is added to the current value.

count[word] ?

When undefined, 1 is assigned to the newly created property.

This if condition doesn’t test for truthy, but equality. counts[word] will be undefined if the key doesn’t exist, as a matter of state. That is one of the very few falsy values in JS.

if (counts[word]) {
    counts[word] += 1
} else {
    counts[word] = 1
}

Switching the clauses around lets us create the same conditional as the ternary.

I wish that was your first reply. In this thread, you branded me in public.

Not really. The question clearly implied unfamiliarity with the subject which raises the flag, “if the person wrote it, then why ask this question?” Sorry if that brings about skepticism. Don’t take it to mean anything given you did write your own solution. We spoke above about review and extra study, for which purpose we are only too happy to explain. Hope this knocks the edge off.

I think I have it now. I should have done this in the first place. A good sleep helped.

let obj1 = {};
obj1['name'] = obj1['name'];
console.log(obj1);//returns {name : undefined}

let obj2 = {};
obj2['name'] = obj2['name'] ? obj2['name'] + 1 : 1;
console.log(obj2); //returns {name: 1} the obscure else is being hit.


let obj3 = {};
obj3['name'] = obj3['name'] ? obj3['name'] + 1 : (obj3['name'] = 1);
console.log(obj3); // returns {name: 1} this is a better version
//in both ternary statements the conditional returns must return falsy on first hit  because the else statement is hit
//then truthy thereafter

//using if else statement
let word = 'really'
let counts = {};
  if (counts[word] = counts[word]) {
    counts[word] = counts[word] + 1;
  } else {
    counts[word] = 1;
  }
console.log(counts);  //returns {really: 1}

Although this does what it should (in a weird way), it is not doing it the same way as the ternary operator:

This is an assignment. An assignment will usually always evaluate to truthy. But in this case – on the first run of a word – if you let it run over an array with a little refactoring, counts[word] is undefined and you cannot assign undefined to undefined. So it goes to the else block.
In the ternary, the first thing after the assignment operator is the condition. The assignment value comes after the ‘?’ or ‘:’.

variable = condition ? assignment if truthy : assignment if falsy
Change this

to this

if (counts[word])

Then it is the same logic.

Thanks for the feed back but the log statement shows the assignment is evaluating to falsy. Take a look at comments in the second code block.

Bottom line: The original ternary operator version is too confusing. A simple if-else is much clearer.

let obj1 = {}; //this just verifies the assignment statement evaluates as falsy
obj1['name'] = obj1['name']; 
console.log((obj1['name'] = obj1['name'])); //logs undefined. The assignment evaluates to undefined

This is my new understanding. It may wrong:

obj2['name'] = obj2['name'] ? obj2['name'] + 1 : 1; // the condition expression is evaluated and the assignment is executed,
                                                      //creates the property 'name' on obj2
                                                      // but evaluates to falsey and triggers the else condition also
console.log(obj2); //logs {name: 1} the else expression was executed and assigned 1 to the name property

I’m not sure to which part of the code line this comment refers, but from the last code block I’m pretty sure that there is a misunderstanding. This is what the ternary does step by step, I’m referring to the part of the code between the asterisks:

**obj2['name']** = obj2['name'] ? obj2['name'] + 1 : 1; 

Left of the assignment operator the property is created

obj2['name'] = **obj2['name']** ? obj2['name'] + 1 : 1;

This is the condition. It is a value. The value is ‘undefined’. That evaluated to falsy.

obj2['name'] = obj2['name'] ? **obj2['name'] + 1** : 1; 

This is the statement – in this case: assignment – if the condition obj2[‘name’] is true/truthy. If it is, it evaluates to 2 for the first value where this is true(thy):
obj2['name'] (evaluates to 1) + 1

obj2['name'] = obj2['name'] ? obj2['name'] + 1 : **1**; 

This is the assignment if the condition is falsy.

Given the code works for arrays, and it runs it’s second iteration:

const words = ['really', 'really', 'no']
const countWords = (word) => counts[word] = counts[word] ? counts[word] + 1 : 1; 

This is what it evaluates to in the 1st iteration:

const counts = {} // at the beginning
counts['really'] = undefined ? NaN + 1 : 1

This is what it evaluates to in the 2nd iteration:

const counts = {
 really: 1 //after the 1st iteration
} 
counts['really'] = 1 ? 2 : 1

I changed the comment to: / the condition expression is evaluated and the assignment is executed

I understand everything up the 2nd to last block: I have no problem with 2nd iteration forward.

“This is what it evaluates to in the 1st iteration:”

const counts = {} // at the beginning
counts['really'] = undefined ? Nan + 1 : 1

This is my understanding now. In the first iteration, the if-condition causes the property really to be added to the counts object. Because the entire if-condition evaluates to falsy, the else expression is executed assigning 1 to the property: really. I hope this is right. I appreciate your time on this.

1 Like

You are referring to my example with the ternary, right? There is no ‘entire if-condition’.
This is the whole ‘if-condition’:

counts[word]

In the first round, the condition evaluates to ‘undefined’, which is falsy, so ‘1’ is assigned.
In the second round, it evaluates to ‘1’, which is truthy, so 1+1 is assigned.