using Short-Circuiting ( || ) in Object in JavaScript - javascript

this block of code takes the values of the scored property and puts them in duplication object as a key and how many times the name got duplicated as a value using the || operator.
I understand that the || operator will return the first truthy value or the last value if all of the values are falsy, however, I didn't understand duplication[x]++ what does the ++ sign do exactly? and why we put the (duplication[x]=1) between parentheses
const game = {
score: "4:0",
scored: ["Lewandowski", "Gnarby", "Lewandowski", "Hummels"],
};
const duplication = {};
for (let x of game.scored) {
duplication[x]++ || (duplication[x]=1) // I'm confused in this line
}
console.log(duplication);

Let's see what's happing on this line :
duplication[x]++ || (duplication[x]=1)
duplication[x]++ , first duplication[x] it will check if duplication has any with value of x, if yes then it it will perform duplication[x]++ else it will be undefined to moved to the other part of or condition
duplication[x]=1, this is a simple assignment it will assign the value 1, duplication[x] and this will create a key if not exist in the duplication object
Now if you run the below script and check the console log for each loop, it will give you clear idea what actually happing.
const game = {
score: "4:0",
scored: ["Lewandowski", "Gnarby", "Lewandowski", "Hummels"],
};
const duplication = {};
let index = 0;
for (let x of game.scored) {
console.log( `INDEX : ${index} ` , x , duplication[x] ? 'FOUND , INCREMENT CURRENT VALUE WITH ++' : 'NOT FOUND, SO ASSIGN VALUE 1' );
duplication[x]++ || (duplication[x]=1)
console.log( `INDEX : ${index} \n` , duplication);
index++;
}
console.log( 'FINAL OBJECT \n' , duplication);

The non-descriptive variable names don't really help to explain the situation. Let's start of by rewriting the code with more descriptive variable names.
const game = {
score: "4:0",
scored: ["Lewandowski", "Gnarby", "Lewandowski", "Hummels"],
};
const goals = {};
for (const player of game.scored) {
goals[player]++ || (goals[player] = 1);
}
console.log(goals);
goals[player]++ increments the goals for player by 1 and returns the old value. The tricky thing in goals[player]++ is that player might not be present in goals yet. In which case undefined is returned (which is falsy). Because the value is falsy the second operand of the OR operator will be executed. (goals[player] = 1) will set the goals for player to 1.
The code is essentially counting how often a specific name is present in the game.scored array. The presence of a name symbolises a goal made by them.
A less cryptic way of writing similar code would be:
const goals = {};
for (const player of game.scored) {
if (player in goals) {
goals[player] += 1; // add 1 to the current score
} else {
goals[player] = 1; // no score present so use 1
}
}
However I usually prefer to set a default value, this way you don't have to split the logic into two actions:
const goals = {};
for (const player of game.scored) {
goals[player] ||= 0; // assign 0 if the current value is falsy
goals[player] += 1; // add 1 to the current value
}
Note that ||= is fairly new, if you write JavaScript for older browser you can use the following instead:
if (!goals[player]) goals[player] = 0;

The first part of
duplication[x]++ || (duplication[x] = 1)
^^^^^^^^^^^^^^^^
has four parts:
a variable duplication with
a property accessor x in bracket notation
a postfix increment operator ++ and
an expression for the logical OR || operator.
The second part returns undefined at the first call with an unknown property.
The try to increment this value returns NaN, because of the following operation of duplication[x] = duplication[x] + 1. The result is is a falsy value.
This forces the expression to evaluate the right hand part of logical OR.
And because the left hand part has an expression, it needs to be evaluated first with a grouping operator (). Now the assignment takes place and the result of 1 is returned to the OR.

Related

Create an object called 'scorers' which contains the names of the players who scored as properties

so i've been working on problem and i found the solution but I would like please someone to explain me further why this works actually
const game = {
team1: 'Bayern Munich',
team2: 'Borrussia Dortmund',
score: '4:0',
scored: ['Lewandowski', 'Gnarby', 'Lewandowski', 'Hummels'],
date: 'Nov 9th, 2037',
odds: {
team1: 1.33,
x: 3.25,
team2: 6.5,
},
};
So, having the above object I was trying to : create an object called 'scorers' which contains the names of the
players who scored as properties, and the number of goals as the value. In this
game.
I came with this solution :
const scorers = {};
for (const player of game.scored) {
scorers[player] ? scorers[player]++ : (scorers[player] = 1);
};
console.log(scorers);
You start by creating a new empty object, then iterate over all the players that are in the game.scored array. What the next line (conditional operator or ternary) does is the following:
if (scorers[player])
scorers[player]++;
else
scorers[player] = 1;
Ternary operators work as follows: <condition> ? <return if true> : <return if false>. You can also chain ternary operators: <condition1> ? <return if true> : (<condition2> ? <return if true> : <return if false>)
In the "if" condition, if you pass only a variable, it compares it with true. Basically its if (scorers[player]===true), which checks if the player key exists in the object scorers. If it does, then it increments its score by 1, if not, it creates it with value 1.
Hope you could understand from my brief explanation!
It works because it's (ab)using the conditional operator purely for the side-effects in its second and third operands, ignoring its result. The first operand is evaluated, then either the second (doing the ++) or third (doing the assignment) is evaluated. scorers[player] will be undefined if you haven't assigned anything for that property yet, which is falsy, so it'll evaluate the third operand (scorers[player] = 1);; if you have assigned something to scorers[player], it'll be a number other than 0, so it'll be truthy, and the operator evaluates the second operand (scorers[player]++) instead.
The code does the same thing this possibly-clearer code does:
const scorers = {};
for (const player of game.scored) {
if (scorers[player]) {
scorers[player]++;
} else {
scorers[player] = 1;
}
}
console.log(scorers);
(Side note: You don't put ; after the block attached to a for.)
Another way you'll commonly see this written is this:
const scorers = {};
for (const player of game.scored) {
scorers[player] = (scorers[player] ?? 0) + 1;
// Or before the `??` operator was added:
// scorers[player] = (scorers[player] || 0) + 1;
}
console.log(scorers);
If scorers[player] is undefined, the nullish coalescing operator (??) will evaluate scorers[player] ?? 0 to be 0, so we add one to it. Otherwise it'll be the value of scorers[player], so we increase it by one.
When you loop through the game.scored. You have the decision logic to check if the scorers[player] have this player yet. if Yes plus one to that player. if Not, add that player to the scorers object and set the value to 1.

Javascript object

I came across a problem in an online course:
Write a function called vowelCount which accepts a string and returns an object with the keys as the vowel and the values as the number of times the vowel appears in the string. This function should be case insensitive so a lowercase letter and uppercase letter should count
Examples:
vowelCount('Elie') // {e:2,i:1};
the solution from the instructor came like this:
function vowelCount(str){
var splitArr = str.toLowerCase().split("");
var obj = {};
var vowels = "aeiou";
splitArr.forEach(function(letter){
if(vowels.indexOf(letter) !== -1){
if(obj[letter]){
obj[letter]++;
} else{
obj[letter] = 1;
}
}
});
return obj;
}
I understand the solution until the second "if" statement. I know that the first "if" statement is to check if the "letters" in the input string belongs to the "vowels". Then in the second "if" it is checking if the "letter is in the empty "obj" object created above, but at that line, the "obj" is empty bofore the "letter" is added to it, so what is the point for that "if". Also, why does adding this new "letter" to the object require an increment. I tried the code without increment and the object is still empty.
It's checking if you've ever seen the letter before in the loop. If you've never written to obj[letter], then when you do obj[letter], you get back the value undefined, which is falsy (treated as false by things like an if). if(obj[letter]) is checking for a truthy value (a value that isn't falsy) so that it adds to the number already stored at obj[letter] if it's there (obj[letter]++). But when it sees a falsy value like undefined, it takes the else branch and sets obj[letter] to 1 because the code knows that letter hasn't been seen before.
Just FWIW, while still entirely valid, that's fairly old-style JavaScript code (circa the ES5 standard, 2009). ES2015 added several features you'd use to solve this problem today:
function vowelCount(str){
// Use a Map to remember how many of each ltter you've
// seen. You could use an object as well, but ideally you'd
// create the object with out a prototype to avoid having
// any conflict with inherited properties from `Object.prototype`.
const counts = new Map(); // const counts = Object.create(null);
// The set of vowels
const vowels = new Set("aeiou");
// Loop through the letters
for (const letter of str) {
// If it's not a vowel...
if (!vowels.has(letter)){
// Count it
const currentCount = counts.get(letter) || 0;
counts.set(letter, currentCount + 1);
// Or using an object:
// const currentCount = counts[letter] || 0;
// counts[letter] = currentCount + 1;
}
});
return counts;
}
We can use regular expression to match vowels in a sentence.
Regular expression to match all occurrence of vowel in a string:/[aeiouAEIOU]+?/g
Below is the working code snippet:
//function that takes string as input
//function returns an object containing vowel count without case in account.
function vowelCount(input) {
//to get vowel count using string.match
var arrVowels =input.match(/[aeiouAEIOU]+?/g);
//acc=accumulator, curr=current value
return arrVowels.reduce(function (acc, curr) {
if (typeof acc[curr.toLowerCase()] == 'undefined') {
acc[curr.toLowerCase()] = 1;
}
else {
acc[curr.toLowerCase()] += 1;
}
return acc;
// the blank object below is default value of the acc (accumulator)
}, {});
}

chineseFood[array[0]] = array[array.length-1];

I don't understand the purpose of this = sign on the sixth line in the code block below. I understand how the argument grabs each index number of the array, I just don't understand why chineseFood[array[0]] = array[array.length-1]; In other words, I don't get the purpose of the equal sign as if it were almost comparing each other to be stored in the empty object that is stored in the variable chineseFood. Could someone please clarify? It would be much appreciated.
function transformFirstAndLast(array) {
var chineseFood = {};
//takes 1st element (at index 0) and sets it to the last element (nth index): array(length-1)
chineseFood[array[0]] = array[array.length - 1];
return chineseFood;
}
console.log( transformFirstAndLast(['Orange', 'Lemon', 'Pork', 'Chicken']) );
Output Below
{Orange: "Chicken"}
The equals sign is not comparison, it is assignment. chineseFood is an object, which means that it can be treated like a dictionary, and its properties can be accessed using the [] operator instead of the . operator:
myObj = {
foo: "bar"
};
console.log(myObj["foo"]); // bar
console.log(myObj.foo); // bar
Likewise, you can also assign properties this way:
myObj = {};
myObj["foo"] = 3;
console.log(myObj["foo"]); // 3
console.log(myObj.foo); // 3
This is what your code is doing. It is retrieving the value of array[array.length-1], which is "Chicken". Then it is assigning this value to the property of chineseFood that has the name represented by array[0], which happens to be "Orange". Thus, the property named Orange on chineseFood is set to array[array.length - 1], which is why chineseFood evaluates to {Orange: "Chicken"}.
This method of accessing properties is especially useful when you don't know the name of the property you will be changing in advance, as is the case with this code, or when you want to create properties that have names that would otherwise be illegal:
myObj = {
".you can't usually use with spaces or start w/ periods": false
};
myObj[".you can't usually use with spaces or start w/ periods"] = true;
console.log(myObj[".you can't usually use with spaces or start w/ periods"]);
// there is no way to read this property the normal way
Basically what is does is:
your object is :
var obj = {Orange: "Chicken"};
And Your array is :
var arr = ['Orange','Lemon','Pork','Chicken']
What this line says is pick first element of the array and check for this prop in object and change its value to last element of array, here:
arr[0] = "orange";
So this line :
obj[arr[0]] can be seen as obj['orange'].
After that you change its value:
Obj[arr[0]] = arr[arr.length-1] which can be written as obj['orange'] = 'chicken'

How does this for/in loop work exactly?

I'm doing a beginner exercise, find the mean/median/mode/range of an array of numbers. I'm on the mode now, and found this:
var store = ['1','2','2','3','4'];
var frequency = {}; // array of frequency.
var max = 0; // holds the max frequency.
var result; // holds the max frequency element.
for(var v in store) {
frequency[store[v]]=(frequency[store[v]] || 0)+1; // increment frequency.
if(frequency[store[v]] > max) { // is this frequency > max so far ?
max = frequency[store[v]]; // update max.
result = store[v]; // update result.
}
}
It works but I don't understand it.
What does the || 0 do in the first line?
Why can't I change the key names?
frequency["key"+store[v]]=(frequency[store[v]] || 0)+1; returns {key1: 1, key2: 1, key3: 1, key4: 1} not {1: 1, 2: 2, 3: 1, 4: 1}, so the keys are playing an important role.
Is the if statement testing both the key and value?
Replacing any instance of frequency[store[v]]; with a variable (var freqTest = frequency[store[v]];, created inside or outside the loop) breaks something.
The whole thing is going over my head really.
The key in the entire logic is understanding this line
frequency[store[v]]=(frequency[store[v]] || 0)+1;
The left side is being used as a map for some number. When v is equal to 3 store[3] returns 2 and thus frequency[2] is accessed.
Now for the same iteration consider the right side. We already know that
frequency[store[3]]
resolves to
frequency[2]
but what will this return? As frequency[2] would have also been set in iteration 2 we would be accessing the number from iteration 2. Lets now look at the value derived from iteration 2 then:
frequency[store[2]] = (frequency[store[2]] || 0)+1
frequency[2] = (frequency[2] || 0)+1
frequency[2] = (null || 0)+1
frequency[2] = 1
Ahhh... so the value for iteration 3 is actually
frequency[2] = (frequency[2] || 0) + 1
frequency[2] = (1 || 0) + 1
frequency[2] = (1) + 1
frequency[2] = 2
As you can see, the loop is using frequency[n] as a map and increments the value each time it is found. Then the value is stored in max if it is higher. This is a very smart way to find the highest repeating value while only iterating over the list one time.
What does the || 0 do in the first line?
It takes 0 as a default value when the lookup fails (when there is not yet a frequency property with that name), so that the map is initialised with 1s on the first appearance of a value, not NaN (from undefined + 1).
The assignment can (and for beginners, should) be expanded to
if (frequency[key]) // the property already exists, does not give `0` or `undefined`
frequency[key] = frequency[key] + 1;
else // the property didn't exist and the access yielded `undefined`
frequency[key] = 1; // 0 + 1
Why can't I change the key names?
You can, you just have to do it everywhere.
The code should be written much cleaner like this:
var store = ['1','2','2','3','4'];
var frequency = {}; // object (key-value-map) of frequency
var max = 0; // holds the max frequency value
var result; // holds the max frequency element name
for (var v=0; v<store.length; v++) {
var key = "key" + store[v];
frequency[key] = (frequency[key] || 0)+1; // increment frequency
// ^^^ here as well
if (frequency[key] > max) { // is this frequency > max so far ?
max = frequency[key]; // update max.
result = store[v]; // update result.
// ^^^^^^^^ alternatively use `key` also here
}
}
Is the if statement testing both the key and value?
Testing? Hm, no. It does use the value from the store array as a key in the frequency object. It does then compare the property value with the max.
a || 0 means if a is not undefined, take 1 otherwise 0
You can change the key names.
var store = ['1','2','2','3', '1', '1','4'];
var frequency = {}; // array of frequency.
var max = 0; // holds the max frequency.
var result; // holds the max frequency element.
for(var v in store) {
frequency['key'+store[v]]=(frequency['key'+store[v]] || 0)+1; // increment frequency.
if(frequency['key' + store[v]] > max) { // is this frequency > max so far ?
max = frequency[store[v]]; // update max.
result = 'key' + store[v]; // update result.
}
}
The line you ask about frequency[store[v]]=(frequency[store[v]] || 0)+1 is sometimes referred to OR assignment; this stack overflow question has some good explanations and examples. For your code, consider this that I just typed into my browser's javascript console:
> var frequency = {};
<- undefined
> frequency[0];
<- undefined
> frequency[0] || 0
<- 0
As for you why you can't change the key names, you can, you just haven't changed them 'enough'. Changing the body to replace every key reference with "key"+store leaves the code in the same functioning state.
for(var v in store) {
// Increment the frequency of the value v
frequency["key"+store[v]]=(frequency["key"+store[v]] || 0)+1;
// is this frequency > max so far ?
if(frequency["key"+store[v]] > max) {
// If it is, we have a new, most frequently occurring number
// Update the max to this new highest frequency
max = frequency["key"+store[v]];
// Update the result to the new most frequent value
result = store[v];
}
}
I added some additional comments in the code to make it clearer what's going on.
To your first question:
In JavaScript you can test if variables are defined by using them as booleans.
var foo;
if(foo) //true
console.log('foo is false, 0, null or not defined');
So in this case you are testing if frequency already has an element for store[v]. If it does, use that, otherwise use 0 instead so that would be the same as
var valueInFrequency = frequency[store[v]] ? frequency[store[v]] : 0;
and then continue with valueInFrequency.
To your second question: As I explained just now, in
frequency[store[v]]=(frequency[store[v]] || 0)+1;
you either raise the current value by one or set it to 0 and then raise it by one. If you change the key you set the value to but then don't test for the new value, you end up simply overriding the existing value to 0 + 1.
Now to your last question: No it isn't. It uses store[v] as a key for frequency and then compares that value to max.
I hope I could answer your questions. If anything is still unclear, just ask!
I propose a better solution to this problem as the given solution.
Solution with emphasis to Array.prototype.forEach and the problem of getting more than one key if the max count is shared among more items.
What has changed:
result is now an array, because the maximal count of the distribution can affect more than one key/item.
The for () loop is replaced by Array.prototype.forEach and a callback which allows an iteration over all ements of an array in a more compact manner.
Only max is in the callback stored.
For the keys/item with the max count is another loop necessary.
First get the keys from the object with Object.keys
Then iterates over the keys and check for count === max and push the key
Display all found values.
To the question what x = x || y means:
If the value of x is falsy (like undefined, null, 0, -0, '') the the value of y is used, because of the Logical Or operator.
var store = ['1', '2', '2', '3', '4', '5', '5'],
distribution = {},
max = 0,
result = [];
store.forEach(function (a) {
distribution[a] = (distribution[a] || 0) + 1;
if (distribution[a] > max) {
max = distribution[a];
}
});
Object.keys(distribution).forEach(function (k) {
distribution[k] === max && result.push(k);
});
document.write('max: ' + max + '<br>');
document.write('key/s with max count: ' + JSON.stringify(result) + '<br>');
document.write('<pre>' + JSON.stringify(distribution, 0, 4) + '</pre>');

Javascript: Reduce function with ||

Can someone explain the following code? inputWords is supposed to be an array containing various words and this function is supposed to return an array containing the number of times a word appears in inputWords.
ie. var inputWords = ['Apple', 'Banana', 'Apple', 'Durian', 'Durian', 'Durian']
console.log(countWords(inputWords))
// =>
// {
// Apple: 2,
// Banana: 1,
// Durian: 3
// }
I understand what the Reduce function does, but what is resultObj[word] = ++resultObj[word] || 1; doing?
Thanks so much :)
function countWords(inputWords) {
return inputWords.reduce(function(resultObj, word) {
resultObj[word] = ++resultObj[word] || 1;
return resultObj;
}, {});
}
module.exports = countWords;
The code attempts to assign to a key (that may not yet exist) an incremented value (of a key that may not yet exist), of if that it that is falsey, it assigns a 1.
This is called short-circuit evaluation. Given a = b || c, if b is truthy, c never gets evaluated so a takes on the value of b. If b is falsey, c is evaluated and assigned to a instead. In your case, when the key doesn't exist, ++resultObj[word] is falsey.
In my humble opinion, I think that it would have been a clearer statement of the author's intention if they had instead done:
if (word in resultObj) {
++resultObj[word];
}
else {
resultObj[word] = 1;
}
or even:
resultObj[word] = word in resultObj ? resultObj[word] + 1 : 1;
either of which would have saved you the bother of asking this question.
It's 'defaulting' a non-existent key in resultObj to 1.
Javascript's || will actually return the first (leftmost) truthy value out of a comparison, so for a nonexistant value added one by the preincrement operator such as ++resultObj['banana'] returns NaN, which is falsey, and the || operator will replace it with 1.
Edit: See http://nfriedly.com/techblog/2009/07/advanced-javascript-operators-and-truthy-falsy/
I would write:
resultObj[word] = (resultObj[word] || 0) + 1;

Categories

Resources