Recently I found out that a simple arrow function, for example callback in this map
[1, 2, 3, 4, 5, 6].map(item => {
console.log(item) // or anything else
return item - 1;
})
I can re-write to one line command like this
[1, 2, 3, 4, 5, 6].map(item => (console.log(item), item - 1))
I can use as much statements I want to devided by , and last argument will always be a return value. It looks kind of cool to me, but cant find anything about this syntax in arrow function documentation. Can anyone explain this syntax or just point to place where I found a docs?
Essentially it allows you to do multiple operations in a single statement. It's used commonly in for loops and to assign multiple variables in a single statment, eg.: var a = 1, b = 2;.
Just because it works though, doesn't mean it's a good idea. It makes your code less readable and harder to debug.
See the MDN docs on the Comma Operator.
The comma operator (,) evaluates each of its operands (from left to right) and returns the value of the last operand. This lets you create a compound expression in which multiple expressions are evaluated, with the compound expression's final value being the value of the rightmost of its member expressions. This is commonly used to provide multiple parameters to a for loop.
You are using the comma operator to evaluate multiple expressions:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
Related
So here is my solution:
let data = [11, null, NaN, 'Hello', 24]
let check = 0;
for (item in data) {
if (isNaN(item) == false) {
check +=1
};
};
console.log(check);
How ever the answer comes five, even though it is 2. I believe isNaN() function gives true or false, so why is it not working?
The in in item in data checks the properties of an object. In an array, the keys are numbers, you have five entries, so you get five times not isNan().
You want to iterate the array, so it should be let item of data
for...in loops over the properties of an object. To loop over an array's elements, use for...of or Array#forEach.
For this specific case, Array#reduce is particularly appropriate.
let data = [11, null, NaN, 'Hello', 24];
let res = data.reduce((a, b) => a + !isNaN(b), 0);
console.log(res);
Your code returns 5 because for...in iterates over the array keys. And the array keys of data are the numbers between and including 0 and 4. Five in total, all of them are numbers.
There are multiple ways to write the code.
Using for...in, use item as an index in data (because this is what it is):
const data = [11, null, NaN, 'Hello', 24]
let check = 0;
for (item in data) {
if (isNaN(data[item]) == false) {
check += 1;
}
}
console.log(check);
Using for...of, item is a value from data and the rest of your code works fine:
const data = [11, null, NaN, 'Hello', 24]
let check = 0;
for (item of data) {
if (isNaN(item) == false) {
check += 1;
}
}
console.log(check);
Using Array.forEach() you get both the index and the value as arguments of the callback function. The value is the first argument, the index is not needed (but it is available as the second argument):
const data = [11, null, NaN, 'Hello', 24]
let check = 0;
data.forEach((item) => {
if (isNaN(item) == false) {
check ++;
}
});
console.log(check);
The callback function used by Array.forEach() changes a variable that is neither received as argument, nor a local variable of the callback function. The code is somewhat confusing this way and it is not properly encapsulated. While this is fine because there is not better option, here it can be done better using Array.reduce(). Array.reduce() seems to be closer to what the program wants to achieve than Array.forEach() (that is just a cleaner way to write the two for loops attended before).
const data = [11, null, NaN, 'Hello', 24]
let check = data.reduce(
(acc, item) => {
if (isNaN(item) == false) {
acc ++;
}
return acc;
},
0
);
console.log(check);
However, Array.reduce() is still a wrong fit. The purpose of the code is to count the numbers from the data array. A more natural way is to not count them but to extract the numbers into a new array using Array.filter(). The array itself tells us how many items it contains by accessing its .length property. There is no need for ifs:
const data = [11, null, NaN, 'Hello', 24]
const check = data.filter((item) => !isNaN(item)).length;
console.log(check);
Other remarks
Do not put semicolons (;) after the close brace } of a code block. The semicolon is a statement separator and a code block can be used wherever a statement can be used. All in all the following piece of code
if (true) {
};
... contains two statements. One is if (true) with an empty code block on the if branch, the other is an empty statement that follows the code block until the semicolon.
It does not have any undesired effect here but it can be the source of hidden bugs difficult to find.
The semicolon is required only after some JavaScript statements. For all the others, a new line works the same as a semicolon. More than that, the JavaScript interpreter is able to insert some missing semicolons automagically.
Attention! I do not say to never use semicolons. Use them where they must be used but be aware that a missing semicolon is either inserted by the interpreter or flagged as a syntax error (and the program does not start) while an extra semicolon in the wrong place can make your program behave in an unexpected way.
Do not use == and !=. They do loose comparisons (f.e. 2 == '2') and sometimes they have surprising results. Always use the strict equality operator (===) and the strict inequality operator (!==). They first check the types of the two operands and do not do confusing conversions to check the values.
x += 1 is usually written x ++. Be aware that, in some contexts, x ++ is has different side effects than ++ x.
There is nothing wrong with += 1, just nobody uses it.
Sometimes, using the logical NOT operator (!) the code becomes more clear. It is abused by many developers that produce "clever" but unclear code but using it here is better than comparing against false.
To me, if (! isNaN(item)) seems easier to understand than if (isNaN(item) === false). It can almost be read: "if item is not NaN" vs. "if item is not NaN is false".
It does not make any difference to the interpreter though. Write the code to be easy to read and understand. The computer does not care.
What does it mean when variable value in round brackets in js?
For example,
let a = (1,2,3);
What does it mean and why console.log(a) output is 3?
What is usage of comma operator in round brackets in variable initialization?
The parentheses are needed for grouping. In a let statement, commas are normally used to separate multiple variables that are being declared, e.g.
let a = 1, b = 2, c;
which is short for
let a = 1;
let b = 2;
let c;
If you write
let a = 1, 2, 3;
you'll get a syntax error, because after the comma it expects another variable declaration; it's equivalent to:
let a = 1;
let 2;
let 3;
The second and third declarations are clearly wrong, as 2 and 3 are not variable names.
The parentheses indicate that the whole expression 1, 2, 3 is being used to initialize one variable.
The expression 1, 2, 3 uses the Comma operator, which executes each of its subexpressions and returns the last one as its value. It's pretty useless when the subexpressions are all constants, so I assume your code was just a simplified example. Because the way it's written, it's really just equivalent to:
let a = 3;
What you have encountered is the comma operator.
Quoting the docs
The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.
Therefore in your case 1, 2 and 3 is evaluated and 3 is returned and thus assigned to the variable a.
This is not variable declaration-specific thing. You can write (1,2,3) anywhere in your JS code and it will always evaluate to 3. The thing is, JavaScript (like many other programming languages, e.g. C) has comma operator, which simply returns last element. The expression (1,2,3) basically looks just like (1+2+3) to JavaScript, except for comma operator is applied instead of addition.
It's call the comma operator:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
It's a very handy thing to use in JavaScript, because it allows you to run multiple statements as a single value for an arrow function.
Here is an example of a 10x10 identity matrix generated with one line of code. The last comma value is returned as the value.
const m = new Array(9).fill('000000000').map((v,i)=> (v=[...v],v[i]='1',v.join()));
console.log(m);
The same above code as a blocked function would take a lot more lines.
const m = new Array(9).fill('000000000').map((v,i)=> {
v = [...v];
v[i] = '1';
return v.join()
});
console.log(m);
my question is about parentheses () in Javascript code, for example in the snippet of code below, are the parentheses 'grouping operators'? or something else? if there are 2 or more sets of parentheses (), which set of () is evaluated first in the same line? have googled but can't find much about this. Although I understand most of this little program, I am struggling to understand lines 5 + 6 as I don't understand this aforementioned parentheses issue, thanks very much for any help!
<script>
function playAudio(audioNumber) {
var audio = document.getElementById('sound' + audioNumber);
audio.play();
var nonPlaying = [1, 2, 3, 4, 5, 6, 7];
nonPlaying.splice(notPlaying.indexOf(audioNumber), 1); // line 5
nonPlaying.forEach(function (id) { // line 6
document.getElementById('sound' + id).pause();
document.getElementById('sound' + id).currentTime = 0;
});
};
</script>
On line 5, the parenthesis follow standard order of operation, just like in math. For
nonPlaying.splice(notPlaying.indexOf(audioNumber), 1);
The call to indexOf is inside of the argument list (parentheses) of splice, so it's evaluated first. This makes sense though since indexOf must be evaluated before its return can be passed to splice.
To answer "which set of () is evaluated first in the same line", that depends on how they're used. In
document.getElementById('sound' + id).pause();
The line is evaluated left to right, although that has little to do with the parentheses. In
a().b()
a must be evaluated first since b is a method of a. The operator associativity of . defines the order.
The only other use is as the parameter list to the anonymous function:
function (id) {...}
Which is a bit of a special case. I don't think there's a situation where you would have nested parentheses in a parameter list; even with destructuring since JS doesn't use () to define any structures.
I've been working for some time on a Javascript FP library called Ramda, and I'm having a slight problem with naming things. (You've heard the old line, right? "There are only two hard problems in Computer Science: cache invalidation, naming things, and off-by-one errors.")
In this library, (almost) every function of more than one parameter is automatically curried. And this works well for most use-cases. But there are some issues with a few functions which are non-commutative binary operators. The issue is that the English names often tend to imply something different than what happens when currying is applied. For example,
var div10 = divide(10);
sounds like it should be a function that divides its parameter by 10. But in fact it divides its parameter into 10, which is pretty clear if you look at the definition:
var divide = curry(function(a, b) {
return a / b;
});
So instead the expected:
div10(50); //=> 5 // NO!!
In fact, you get
div10(50); //=> 0.2 // Correct, but surprising!
We handle this by documenting the difference from people's possible expectations, and creating divideBy, which is just flip(divide) and subtractN, which is flip(subtract). But we haven't found a good equivalent for functions such as lt:
R.lt = curry(function(a, b) {
return a < b;
});
or its cousins lte, gt, and gte.
My own intuition would be that
map(lt(5), [8, 6, 7, 5, 3, 0, 9]);
//=> [false, false, false, false, true, true, false]
But of course, it actually returns
//=> [true, true, true, false, false, false, true]
So I'd like to do the same document-and-point-to-alternate-name routine for lt and its ilk. But I haven't been able to find a good name. The only real candidate has been ltVal and that doesn't really work when called with both arguments. We did discuss this issue, but had no good conclusions.
Have others dealt with this and come up with good solutions? Or even if not, any good suggestions for a name for the flipped versions of these functions?
Update
Someone suggested that this be closed because 'unclear what you were asking', and I guess the question really was lost a bit in the explanation. The simple question is:
What would be a good, intuitive name for a flipped version of lt?
First of all, kudos on the functional programming library that you are maintaining. I've always wanted to write one myself, but I've never found the time to do so.
Considering the fact that you are writing a functional programming library I'm going to assume that you know about Haskell. In Haskell we have functions and operators. Functions are always prefix. Operators are always infix.
Functions in Haskell can be converted into operators using backticks. For example div 6 3 can be written as 6 `div` 3. Similarly operators can be converted into functions using parentheses. For example 2 < 3 can be written as (<) 2 3.
Operators can also be partially applied using sections. There are two types of sections: left sections (e.g. (2 <) and (6 `div`)) and right sections (e.g. (< 3) and (`div` 3)). Left sections are translated as follows: (2 <) becomes (<) 2. Right sections: (< 3) becomes flip (<) 3.
In JavaScript we only have functions. There is no “good” way to create operators in JavaScript. You can write code like (2).lt(3), but in my humble opinion it is uncouth and I would strongly advise against writing code like that.
So trivially we can write normal functions and operators as functions:
div(6, 3) // normal function: div 6 3
lt(2, 3) // operator as a function: (<) 2 3
Writing and implementing infix operators in JavaScript is a pain. Hence we won't have the following:
(6).div(3) // function as an operator: 6 `div` 3
(2).lt(3) // normal operator: 2 < 3
However sections are important. Let's start with the right section:
div(3) // right section: (`div` 3)
lt(3) // right section: (< 3)
When I see div(3) I would expect it to be a right section (i.e. it should behave as (`div` 3)). Hence, according to the principle of least astonishment, this is the way it should be implemented.
Now comes the question of left sections. If div(3) is a right section then what should a left section look like? In my humble opinion it should look like this:
div(6, _) // left section: (6 `div`)
lt(2, _) // left section: (2 <)
To me this reads as “divide 6 by something” and “is 2 lesser than something?” I prefer this way because it is explicit. According to The Zen of Python, “Explicit is better than implicit.”
So how does this affect existing code? For example, consider the filter function. To filter the odd numbers in a list we would write filter(odd, list). For such a function does currying work as expected? For example, how would we write a filterOdd function?
var filterOdd = filter(odd); // expected solution
var filterOdd = filter(odd, _); // left section, astonished?
According to the principle of least astonishment it should simply be filter(odd). The filter function is not meant to be used as an operator. Hence the programmer should not be forced to use it as a left section. There should be a clear distinction between functions and “function operators”.
Fortunately distinguishing between functions and function operators is pretty intuitive. For example, the filter function is clearly not a function operator:
filter odd list -- filter the odd numbers from the list; makes sense
odd `filter` list -- odd filter of list? huh?
On the other hand the elem function is clearly a function operator:
list `elem` n -- element n of the list; makes sense
elem list n -- element list, n? huh?
It's important to note that this distinction is only possible because functions and function operators are mutually exclusive. It stands to reason that given a function it may either be a normal function or else a function operator, but not both.
It's interesting to note that given a binary function if you flip its arguments then it becomes a binary operator and vice versa. For example consider the flipped variants of filter and elem:
list `filter` odd -- now filter makes sense an an operator
elem n list -- now elem makes sense as a function
In fact this could be generalized for any n-arity function were n is greater than 1. You see, every function has a primary argument. Trivially, for unary functions this distinction is irrelevant. However for non-unary functions this distinction is important.
If the primary argument of the function comes at the end of the argument list then the function is a normal function (e.g. filter odd list where list is the primary argument). Having the primary argument at the end of the list is necessary for function composition.
If the primary argument of the function comes at the beginning of the argument list then the function is a function operator (e.g. list `elem` n where list is the primary argument).
Operators are analogous to methods in OOP and the primary argument is analogous to the object of the method. For example list `elem` n would be written as list.elem(n) in OOP. Chaining methods in OOP is analogous to function composition chains in FP[1].
The primary argument of the function may only be either at the beginning or at the end of the argument list. It wouldn't make sense for it to be anywhere else. This property is vacuously true for binary functions. Hence flipping binary functions makes them operators and vice-versa.
The rest of the arguments along with the function form an indivisible atom called the stem of the argument list. For example in filter odd list the stem is filter odd. In list `elem` n the stem is (`elem` n).
The order and the elements of the stem must remain unchanged for the expression to make sense. This is why odd `filter` list and elem list n don't make any sense. However list `filter` odd and elem n list make sense because the stem is unchanged.
Coming back to the main topic, since functions and function operators are mutually exclusive you could simply treat function operators differently than the way you treat normal functions.
We want operators to have the following behavior:
div(6, 3) // normal operator: 6 `div` 3
div(6, _) // left section: (6 `div`)
div(3) // right section: (`div` 3)
We want to define operators as follows:
var div = op(function (a, b) {
return a / b;
});
The definition of the op function is simple:
function op(f) {
var length = f.length, _; // we want underscore to be undefined
if (length < 2) throw new Error("Expected binary function.");
var left = R.curry(f), right = R.curry(R.flip(f));
return function (a, b) {
switch (arguments.length) {
case 0: throw new Error("No arguments.");
case 1: return right(a);
case 2: if (b === _) return left(a);
default: return left.apply(null, arguments);
}
};
}
The op function is similar to using backticks to convert a function into a operator in Haskell. Hence you could add it as a standard library function for Ramda. Also mention in the docs that the primary argument of an operator should be the first argument (i.e. it should look like OOP, not FP).
[1] On a side note it would be awesome if Ramda allowed you to compose functions as though it was chaining methods in regular JavaScript (e.g. foo(a, b).bar(c) instead of compose(bar(c), foo(a, b))). It's difficult, but doable.
We all know that naming in programming is serious business, particularly when it comes to functions in curried form. It is a valid solution to handle this issue with a programmatic approach as Aadit did in his response. However, I see two issues with his implementation:
it introduces function operators with left/right sections in Javascript, which are not part of the language
it requires a hideous placeholder or undefined hack to achieve that
Javascript has no curried operators and thus no left or right sections. An idiomatic Javascript solution should consider that.
The cause of the problem
Curried functions don't have a notion of arity, because every function invocation requires exactly one argument. You can either partially or completely apply curried functions without any helpers:
const add = y => x => x + y;
const add2 = add(2); // partial application
add(2)(3); // complete application
Usually the last argument of a function is its primarily one, because it is passed through function compositions (similar to objects that allow method chaining). Consequently when you partially apply a function, you want to pass its initial arguments:
const comp = f => g => x => f(g(x));
const map = f => xs => xs.map(x => f(x));
const inc = x => x + 1;
const sqr = x => x * x;
comp(map(inc)) (map(sqr)) ([1,2,3]); // [2,5,10]
Operator functions are special in this regard. They are binary functions that reduce their two arguments to a single return value. Since not every operator is commutative (a - b !== b - a) the argument order matters. For this reason operator functions don't have a primarily argument. But people are accustomed to read expressions with them in a certain way depending on the type of application:
const concat = y => xs => xs.concat(y);
const sub = y => x => x - y;
// partial application:
const concat4 = concat(4);
const sub4 = sub(4);
concat4([1,2,3]); // [1,2,3,4] - OK
sub4(3); // -1 - OK
// complete application:
concat([1,2,3]) (4); // [4,1,2,3] - ouch!
sub(4) (3); // -1 - ouch!
We defined concat and sub with flipped arguments, so that partial application works as expected. This evidently doesn't apply to complete application though.
A manual solution
const flip = f => y => x => f(x) (y);
const concat_ = flip(concat);
const sub_ = flip(sub);
concat_(xs) (4); // [1,2,3,4] - OK
sub_(4) (3); // 1 - OK
concat_ and sub_ correspond to left sections in Haskell. Please note that function operators like add or lt don't need a left section version because the former are commutative and the latter are predicates, which have logical counterparts:
const comp2 = f => g => x => y => f(g(x) (y));
const map = f => xs => xs.map(x => f(x));
const flip = f => y => x => f(x) (y);
const not = x => !x;
const notf2 = comp2(not);
const lt = y => x => x < y;
const gt = flip(lt);
const lte = notf2(gt);
const gte = notf2(lt);
map(lt(5)) ([8, 6, 7, 5, 3, 0, 9]);
// [false, false, false, false, true, true, false]
map(gte(5)) ([8, 6, 7, 5, 3, 0, 9]);
// [true, true, true, true, false, false, true]
Conclusion
We should solve this naming issue rather with a naming convention then with a programmatic solution which extends Javascript in a non-idiomatic way. Naming conventions are not ideal...well, just like Javascript.
I came across a piece of code that is:
for(i=((90.0E1,0x5A)<=(0x158,140.70E1)?(.28,3.45E2,0):(95.30E1,26.40E1)<=1.400E2?(1,this):(108.,0x227));i<length;i++) {
// some other code here
}
Can someone help me by explaining the stuff in the for() brackets?
The result of the comma operator is always the value to the right hand side.
So each pair of the form (a,b) evaluates to b. Since in your code "a" never has a side effect, we can
simply omit it to get:
for(i=(0x5A <= 140.70E1 ? 0 : ...);i<length;i++) {
Where "..." stands for stuff that doesn't matter:
Since 0x5A <= 140.70E1 evaluates to true, the result of the ?: operator is the value to the right of the question mark, i.e. 0.
So the result is equivalent to
for (i=0; i<length; i++) {
which makes sense.
It is a standard three-expression for statement, where the first expression, the initializer, happens to be defined as
i = ((90.0E1,0x5A)<=(0x158,140.70E1)?(.28,3.45E2,0):(95.30E1,26.40E1)<=1.400E2?(1,this):(108.,0x227))
In this expression, the ternary ?: operator, and, to complicate things, does this in a nested fashion.
The syntax of the ?: operator is the following
condition ? value if true : value if false
Given this, the expression is composed of the following
condition: (90.0E1,0x5A)<=(0x158,140.70E1)
value if true: (.28,3.45E2,0)
value if false: (95.30E1,26.40E1)<=1.400E2?(1,this):(108.,0x227)
The value-if-false holds a nested expression using the ?: operator which can of course be deconstructed in the same way.
Simplifying the hex and E numbers, it becomes:
for(i=((900,90)<=(344,1407)?(.28,345,0):(953,264)<=140?(1,this):(108.,551));i<length;i++)
((900,90)<=(344,1407)?(.28,345,0):(953,264)<=140?(1,this):(108.,551)) == 0;
which makes the code equivalent to:
for(i=0;i<length;i++)
It's a very creative and confusing way to make a for loop, and a good joke.