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.
Related
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
Some background for this coding problem. Our termTopics function needs to count how many times each of those topics was mentioned in the surveys, and then return an array with the number of mentions in the following order: smart city, arts funding, and then transportation.
const termTopics = (interviews) => {
const count = interviews.reduce((acc, cv) => {
return {...acc, [cv]: acc[cv] ? acc[cv]+1 : 1}
}, {})
return [count['smart city'], count['arts funding'], count['transportation']];
}
What I cannot understand is the spread operator, and how that creates a truthy statement for the ternary operator to operate with.
const count = interviews
.reduce((resultObject, interview) => {
// We are creating an object through the reduce function by iterating through the interviews array.
// At each iteration we will modify the result object according to the current array interview item value
return {
// First we copy the object we have so far
...resultObject,
// Then we set the property corresponding to the current item
[interview]: resultObject[interview]
// If it is not the first time we have seen this item, the object property already exists and we update it by adding one to its value
? resultObject[interview] + 1
// Otherwise we create a new property and set it to one
: 1
}
}, {})
The spread syntax and the conditional operator are completely unrelated here. Let's unfold back to forth here:
The entire conditional operator expression is acc[cv] ? acc[cv]+1 : 1, so it will resolve to only acc[cv]+1 or 1 based on whether or not acc[cv] is truthy or not.
The result of the conditional operator is assigned to a property in an object.
The property name is [cv] - a computed property name that will equal the current value of cv.
The property name and value are added to an object.
The rest of the object values are ...acc or the current values of the object that are spread into the object.
In effect, {...acc, [cv]: acc[cv] ? acc[cv]+1 : 1} is a shorter version of the following ES5 code:
var result = {};
//{...acc}
for (var key in acc) {
result[key] = acc[key];
}
//[cv]: acc[cv] ? acc[cv]+1 : 1
if (acc[cv]) {
result[cv] = acc[cv]+1;
} else {
result[cv] = 1;
}
The truthy (or falsy) value is coming from here: acc[cv] and if there's a value you increment it by one, otherwise you set it to one.
acc[cv] is a computed property and will look up the value of cv as a property of acc, eg acc['smart city'].
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);
}
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).
In the source code of zepto,I found that in zepto.matches function,there's a fallback perform,
zepto.matches = function(element, selector) {
if (!element || element.nodeType !== 1) return false
var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector ||
element.oMatchesSelector || element.matchesSelector
if (matchesSelector) return matchesSelector.call(element, selector)
// fall back to performing a selector:
var match, parent = element.parentNode, temp = !parent
if (temp) (parent = tempParent).appendChild(element)
match = ~zepto.qsa(parent, selector).indexOf(element)
temp && tempParent.removeChild(element)
return match
}
despite the normal matchSelector way,I'm quite curious about the fallback,why it add ~ in front of this:
~zepto.qsa(parent, selector).indexOf(element)
document.getElementById("test");
$.zepto.matches(a,"#test") // -1
it returns -1 , is it right or am I missing something?
It's the Bitwise NOT operator. In this case it's used to have a boolean return value for indexOf: the indexOf methods (array, string), returns -1 if a match is not found, and the position if a match is found. The position can be also 0, of course. So using the bitwise NOT operator:
console.log(~-1); // 0
console.log(~0) // -1
console.log(~1) // -2
// and so on
So because in JS 0 is considered as a "Falsy" value, and any other number – except NaN – is a "Truthy" value, having ~zepto.qsa(parent, selector).indexOf(element) will returns 0 if the element is NOT found, and therefore can be thread has a "Falsy" value in the code:
var isFound = ~zepto.qsa(parent, selector).indexOf(element);
if (isFound) {
// do something
}
However, in that case I would expect the zepto methods to return a real boolean, because it's an API method. In this such case, I would prefer use simply:
match = zepto.qsa(parent, selector).indexOf(element) > -1;
Or
match = !!~zepto.qsa(parent, selector).indexOf(element);
The double ! it's used to have the proper Boolean representation: if indexOf returns -1, the ~ transform it in 0, then the first ! in true, and the second ones in false, that is the Boolean value we expect for "not found".
The Bitwise NOT it's also used for some other tricks, like this one:
var words = ["a", "b", "c", "b", "b"];
var frequency = words.reduce(function(freq, word) {
freq[word] = -~freq[word];
return freq;
}, {})
This code returns an object like {a:1, b:3, c:1}; it basically counts the frequency of the same string value in an array. Here the Bitwise NOT it's used with the Unary Negation operator to have an incremental counter: so if freq[word] is undefined – because this word is not yet in the "dictionary" – then the ~undefined is -1, and with the unary negation becames 1. The next time we'll have -~1, and therefore - -2, so 2; then -~2 will be 3 and so on.
The bitwise operators are really useful, but also easier to be abused. In some cases are necessary (For instance, the Zero-fill right shift it's currently the only way in JS to have a substitute of ToUint32) they're not, and they could lead to code hard to read.
But they're definitely worthy to be known.