Clarification on For/Over Loop with Conditionals - javascript

Reading through O'Reilly's JS Definitive Guide and came across this block of code:
let freq = {};
for (let item of "alabama") {
if (freq[item]) {
freq[item]++;
} else {
freq[item] = 1;
}
}
Just want to go over some of the the syntax and implications:
Assigning an empty object to "freq" variable
Running the for/of loop over the given string
If statement checks if freq[item] returns true .. I get that part but what will trigger that truthy value?
And thus what how would a falsy value be triggered to produce a value of 1?
Thank in advance!

First, keep in mind that when iterating over a string with for..of, the item declared for each loop (which you've named item) is each character of the string.
Since the object starts out empty, freq[item] will initially be undefined. For example, on the first iteration, {}['a'] is undefined, which is falsey, so the else is entered:
freq['a'] = 1;
On subsequent iterations, when the character a is found, the a property will exist on the object, so the if is entered, incrementing that property value:
freq['a']++;

The first time you find a letter not in the object it will return undefined
1) a
freq['a'] will be undefined
therefore the code will set a 1 to it
freq['a'] = 1
2) l will go through the same steps as #1
3) a
freq['a'] will be 1
so it's truthy therfore we add 1 to it
freg['a'] ++; which will make it 2
Then you can follow same pattern to figure out the rest

in javascript the following are false
"",false,0,undefined,null..
in you case freq is an empty object
freq ={}
in first iteration of loop
item = 'a'
freq[item] will be undefined
if freq[item] will be false
so in else freq[item] = 1 ie. freq={a:1}
same way for second iteration freq={a:1,l:1}
for third iteration
item = 'a'
freq[item] will be 1
if freq[item] will be true and increments freq={a:2,l:1}

Related

Ternary operator condition

The following code uses the reduce method. It outputs the number of times an element appears in the array. If element appears once then it only outputs 1, otherwise if it is a repeated item then it it is added..
let a = ["a", "b", "c", "a", "b"]
const t = a.reduce((aa, ll) => {
const count = aa[ll];
count
?
aa[ll] = count + 1 :
aa[ll] = 1
return aa
}, {})
console.log(JSON.stringify(t))
// output
// { "a":2, "b":2, "c":1 }
Question is regarding the condition in the ternary operation, specifically the count variable. How is the count variable able to resolve true or false.
The concept is called "Truthy" and "Falsy" respectively. Ie anything that is different from false, 0, -0, 0n, NaN, null, undefined and "" (empty string) can be evaluated to true in Javascript
So your assign var counter = aa[ll] to be the value of the key ll in object aa. That's either a number or undefined. If it's a number !== 0 it's a truthy, if it's 0 or undefined it's falsy. Thus it can be used in a ternary operatator. Depending on the value of counter either the value of the first or the second assignment will be returned (but ignored). The return value of an assignment is always the value of the right hand side ...
While you can use also assignments in the expressions of a ternary operator, I personally wouldn't use it that way but write this assignment as follows
const t = a.reduce((aa, ll) => {
aa[ll] = (aa[ll] || 0) + 1;
return aa
}, {})
(aa[ll] || 0) will return the value of aa[ll] if it's a truthy or 0 otherwise. Thus, in your case, the result of this expression will always be a number >= 0. Then you increase the result by 1 (for the currenct occurence of ll) and assign it back to aa[ll]. This is much shorter than your original code and IMHO much more readable
Heres the answer I found.
"A javascript object consists of key-value pairs where keys are unique. If you try to add a duplicate key with a different value, then the older value for that key is overwritten by the new value."
basically the count variable was checking to see if the new property already exists.

delete occurrences of an element if it occurs more than n times javascript

So I have this problem which I have been stuck on but have an answer but not sure exactly why it's working... Please can someone explain why this works?
function deleteNth(arr,n){
var cache = {};
return arr.filter(num => {
cache[num] = (cache[num]||0) + 1; // this is the line i am comfused about!!
return cache[num] <= n;
});
}
deleteNth([1,1,3,3,7,2,2,2,2], 3);
So through the filter function it'll run for each of the elements in the array.
The line you are confused about is setting the cache[num].
In the first iteration, num will be 1, and cache will be equal to {}, so cache[num] will be undefined.
It is setting it to (cache[num] || 0) which in real terms means if cache[num] OR 0. As cache[num] is undefined in the first instance, it'll be 0. It is then adding 1.
so each time the number is hit, it is adding one, and then it will return if the number of instances is below or equal the accepted number, in this case 3.
When it goes above that threshold, it'll be equal to false and now it won't be included as part of the filter.
cache[num]||0 means that if cache[num] doesn't have a key num then use the value 0.
This happens because the value cache[num] can be null in the first occurence
var cache = {}
console.log(cache[1]); // undefined
console.log(cache[1] || 0); // 0

value in array that occurs odd number of times- what is happening here?

I came across this code when I checked how to find the number that occurs odd number of times.
I tried to understand everything but I ca figure this out.
Please tell what is happening step by step and what each variable means.
Thank you!
function findOdd(A) {
let counts = A.reduce((p, n) => (p[n] = ++p[n] || 1, p), {});
return +Object.keys(counts).find(k => counts[k] % 2) || undefined;
reduce is an Array method which should reduce an array to a single value, which could be an array itself, an object or any flat variable type
reduce will take a function and an initial accumulating object as parameters
The function is called for each element in the array and be passed the accumulating object as first parameter (p) and the single array item as second parameter (n)
The function is in this case an arrow function expression
The function body consists of two expressions connected by the comma operator. The comma operator will return the rightmost expression as a result, in this case p.
The first expression of the comma operator (p[n] = ++p[n] || 1) will return 1 if p[n] is not set, otherwise will increment p[n] and return the result. Thanks to the short-circuit evaluation of the logical OR (||)
This could be written a little bit more detailed as
A.reduce(function (p, n) {
if (p[n]) {
p[n] = p[n] + 1
} else {
p[n] = 1
}
return p
}, {});
As a result you receive an object which counts the appearance of every value in the array
Then you have the return statement which will return the first key of counts which has an odd value
It does this by first creating an Array of the keys of counts with Object.keys.
It then iterates over those keys and checks, whether the modulo division by 2 (% 2) of the value corresponding to every key is a truthy value (in this case not zero) and then return that key.
It will always return the first key with that property
At the end this found value is converted to a number with the unary plus operator
If no value was found, undefined is returned
2 methods are used are used here that you need to understand.
reduce : read about it here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
find read about it here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
the below statement makes a map of a number vs its occurence
let counts = A.reduce((p, n) => (p[n] = ++p[n] || 1, p), {})
and then the second statement searches the map for an element which occurs odd number of times and if no such element it found it returns undefined
return +Object.keys(counts).find(k => counts[k] % 2) || undefined

.filter(v => !!v) in Javascript

I have an application that can turns a tex file into a JavaScript object, with key-value pairs. The key being the word and the value being the number of times it has appeared in the text file. Let's go through it together:
FormatText.prototype.toDowncase = function() {
return this._data = this._data.toLowerCase();
};
This turns the words to lowercase
FormatText.prototype.deleteWords = function() {
return this._data = this._data.replace(/\W/g, " ");
};
This replaces all non-words with a space
FormatText.prototype.splitWords = function() {
return this._data = this._data.split(/\s+/);
};
This turns the string in an array and splits at each delimiter
FormatText.prototype.filterEntries = function() {
return this._data = this._data.filter(v => !!v);
};
This one above I have no clue what it does.
FormatText.prototype.countWords = function() {
return this._data = this._data.reduce((dict, v) => {dict[v] = v in dict ? dict[v] + 1 : 1; return dict}, {});
}
Could someone explain this one, however I will get it a try:
This one takes the array and passed the method 'reduce' with two arguments. It counts how many times each individual word has appeared and returns an object with the 'key-value' pairs described at the beginning of this question.
v => !!v means take v, and coerce it to a Boolean type by applying NOT twice. So the filter function is basically removing any falsey values (0, null, undefined) from this._data.
countWords is counting the number of times each word occurs in this._data - it is going through the array and adding 1 to the count if the word has been encountered before, or returning 1 if the word has not been encountered before. It returns an object with the words as keys and the counts as values.
As a note, these functions change the type of this._data, from a string, to an array, to an object. That may cause bugs to appear if e.g. you run the same method twice
Why not just return the value, without NOT NOT, like
v => v
because for filtering the value coerces to a boolean value.
From Array#filter:
Description
filter() calls a provided callback function once for each element in an array, and constructs a new array of all the values for which callback returns a value that coerces to true. callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values. Array elements which do not pass the callback test are simply skipped, and are not included in the new array.
In this case the double exclamation mark is useless: the value returned from the callback in filter(callback) is then coerced to a boolean automatically, so no need to do it using double exclamation mark. The following lines are equivalent:
.filter(v => !!v)
.filter(v => v)
.filter(Boolean)
This one above I have no clue what it does.
The javascript operator ! (logical not) performs a type coercion (to boolean) on its argument. So applied twice you somehow convert any type to a boolean value which gives you whether it is falsy or truthy.
This is interesting when you want to apply a condition to different types whose semantic is more or less "no value". For example:
!!('') //false
!!(0) //false
!!null //false
!!undefined //false
Could someone explain this one, however I will get it a try
reduce is method of the array prototype which allows to iterate over a collection while aggregating value.
In your specific example the aggregator is a dictionary which maps a word to a count (number of appearance). So if the word is not present in the dictionary it creates a key for this word with a counter initialized to 1 otherwise it increments the counter (if word already present).
A equivalent could be
const countWords = function (words = [], dictionary = {}) {
if(words.length === 0) {
return dictionary;
}
const word = words.pop(); //remove and read the word at the end of the array
if(word in dictionary) {//if key is present in the dictionary
dictionary[word] += 1; // increment
else {
dictionary[word] = 1; // start a counter for new keyword
}
return countWords(words, dictionary);
}

how this for loop determines when to break the loop

I have encountered a different type of for loop which i don't see or use commonly.I tried to figure it out but get even more confused in the process.It doesn't have its third argument or even a check method to break the loop.Here it iterates over an array and prints its value.Actually it encounters 'undefined' value for a certain index but i am not telling it to break when it will encounter undefined.please help me to break the puzzle here...
(function () {
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0, value; value = a[i++];) {
document.write(value + '</br>');
}
})();
In javascript, when you access array elements beyond the length of the array, you don't get a range check error, the returned value is undefined which corresponds to false when treated as boolean - thus the loop termination when the end of the array is reached.
If any of the array elements is undefined or any other value that becomes false as boolean, the loop will terminate on that element.
The assignment operator in javascript returns a value of the left side, so expression value = a[i++] is used to return the value of a[i] and increment i - in that order. If this value converts to false as boolean, the loop is terminated.
All arguments to for loops are optional.
The first statement in a for loop is just a variable declaration, as such you are allowed to define multiple variables. The author could've instead written:
var a=[1,2,3,4,5,6,7,8,9];
var value;
for(var i = 0; value = a[i++];)
but went for brevity instead.
The third statement (increment/decrement) is optional, the author (again going for absolute brevity) decided to use postfix increment (i++ will return i THEN increment it, whereas ++i will increment THEN return the incremented value).
They also could've written this:
(function () {
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var value;
var i = 0;
for ( ; value = a[i++]; ) {
document.write(value + '</br>');
}
})();
Finally, a[a.length+1] which would be the final condition evaluated returns undefined, which is a falsy value and will cause the loop to terminate.
A couple of things to note:
undefined is a "falsy" (not-true) value
A loop terminates when its condition becomes non-true
Assigning a[i] (or a[i++]) to value returns value
So, when i == 9, a[i++] == undefined, so value == undefined, so the loop terminates.
for (var i = 0, value; value = a[i++];) {
the second part of for is evaluated as a condition for each iteration.
automatic type conversion happens here so that the value of a[i++] is evaluated. if a[i++] is true the loop continues, if not it stops.
For loop repeats until condition after the first ; is true. In your case eventually after the last element in the array a[i++] will become falsy (ToBoolean(undefined)). Once it happens loop stops.
Take a look at specification for For Statement:
Repeat
a. If the first Expression is present, then
i. Let testExprRef be the result of evaluating the first Expression.
ii. If ToBoolean(GetValue(testExprRef)) is false, return (normal, V, empty).

Categories