Let f and g be two function. Then f() || g() first evaluates f. If the return value of f is falsy it then evaluates g, and returns g's return value.
I love the neat and concise syntax, but it doesn't include the case where f returns the empty array [], which I want to consider "falsy".
Is there clean way of having this syntax for [] instead of the traditional falsy values?
You could write a function that converts the empty array into a real falsy value, maybe?
function e(a) { return a instanceof Array ? (a.length ? a : false) : a; }
var result = e(f()) || g();
The problem with the other solutions presented is that it doesn't behave exactly how you may want the short-circuiting to work. For example, converting the value of f() to a truthy value before the || operator means you lose the ability of returning the result of f(). So here's my preferred solution: write a function that behaves like the || operator.
// let's call the function "either" so that we can read it as "either f or g"
function either () {
var item;
// return the first non-empty truthy value or continue:
for (var i=0;i<arguments.length;i++) {
item = arguments[i];
if (item.length === 0 || !item) continue
return item;
}
return false;
}
We can now use the either() function like how we would the || operator:
either(f(), g());
Why not simply do the length check at f()?
function f(){
var returned_array = new Array();
...
if(!returned_array.length)
return false;
}
If you're talking about actually overloading the || operator, you cannot do that in JavaScript (it was proposed back in ECMAScript 4 but rejected). If you really want to do operator overloading in JavaScript, you'd have to use something like JavaScript Shaper, which is "an extensible framework for JavaScript syntax tree shaping" - you could actually use this to overload operators.
Related
I can't explain it much in the title but I'm now stuck with having to test 2 variables for the same value and was wondering is there a way to do something like this.
var a = 'abc';
var b = 'def';
if (a=='abc' || b=='abc'){ c = 'z'; }
Now c is the variable that was tested true, so by assigning a value to c it would assign the value to the variable that tested true in the if statement. a and b can have different values but both can have the same value aswell.
No, the expression just yields a result value, with no information stored about which subexpression was truthy. (And separately, JavaScript doesn't have references to variables [at least, not that you can use in your code].) You'll have to break it out into two ifs:
if (a === "abc") {
a = "z";
} else if (b === "abc") {
b = "z";
}
It would be possible to do something like this if the variables were accessor properties (or other function calls) with side effects (since the right-hand operand of the || won't be evaluated at all if the left-hand operand evaluates to a truthy value). Or if they were properties in an object and you tested an array of property names, etc. But it would be unnecessarily complicated. :-)
You could take a switch statement, here you can check various variables agains a certain value.
switch ('abc') {
case a:
a = 'z';
break;
case b:
b = 'z';
break;
}
I want to add some defensive coding to the following check. I have 3 strings and I want to know if any of them have anything in them (for my purposes, null or undefined means they do not have anything in them).
if (twitterUrl.length + facebookUrl.length + linkedInUrl.length > 0) {
This works, but feels like very bulky. I use TypeScript and not sure if there is anything there that can help me with this.
if ((twitterUrl ? twitterUrl.length : 0) +
(facebookUrl ? facebookUrl.length : 0) +
(linkedInUrl ? linkedInUrl.length : 0) > 0) {
You can use the fact that empty strings are falsy¹. If you know they'll be strings or null or undefined and you don't need to worry about strings with just whitespace in them (" " is truthy¹), then:
if (twitterUrl || facebookUrl || linkedInUrl) {
If you need to worry about trimming, then a helper function is probably in order:
function present(s) {
return s && (typeof s !== "string" || s.trim());
}
and
if (present(twitterUrl) || present(facebookUrl) || present(linkedInUrl)) {
or
if ([twitterUrl, facebookUrl, linkedInUrl].some(present)) {
¹ falsy and truthy: When you use a value in a condition (like an if), JavaScript will implicitly coerce the value to a boolean. A value that coerces to false is falsy; one that coerces to true is truthy. The falsy values are "", null, undefined, 0, NaN, and of course, false. All other values (including " ") are truthy.
You could define a function as the following one:
function getLength(s){
if(typeof s !== "string") return 0;
return s.length;
}
and then use it like below:
if (getLength(twitterUrl) > 0 || getLenght(facebookUrr) > 0 || getLength(linkedInUrl){
// code
}
Essentially, getLength check if the value you pass when you call the function is a string and if so it returns its length. Otherwise, it returns 0. So in order to achieve that you want, (I want to know if any of them have anything in them), you have to check one by one the strings you have, if the first string has a length greater than zero, there isn't any need to continue the check for the other two strings. Otherwise you call the function on the second string and so on and so forth.
Try like this, normal if statement also works
const socialLinks = [twitterUrl, facebookUrl, linkedInUrl];
const hasSomething = socialLinks.some(social => social);
Here is falsy value like null, undefined, '' and etc., https://developer.mozilla.org/en-US/docs/Glossary/Falsy
if social are empty string('') or null or undefined then it's return false. We omitted return keyword because arrow function has implicit return behaviour.
This is a solution using some(), which checks whether at least one element in the array passes the test implemented by the provided function.
var twitterUrl, facebookUrl, linkedInUrl;
linkedInUrl = 'nonEmpty';
result = [twitterUrl, facebookUrl, linkedInUrl].some(arrVal => arrVal);
console.log(result);
The general question I suppose is: when does || return the item on the left, and when does it return the item on the right?
The specific question, is why doesn't this work:
var fibonacci = (function () {
var cache = [0, 1];
function fibonacci(number) {
return cache[number] = cache[number] || (fibnonacci(number - 1) + fibonacci(number - 2));
}
return fibonacci;
})();
var $div = $('div');
for (var index = 0; index < 10; index++) {
$('<span />').text(fibonacci(index))
.appendTo($div);
}
It returns the item on the left if and only if it is truthy.
The following are not truthy:
The primitive boolean value false
The primitive string value "" (the empty string)
the numbers +0, -0 and NaN
the primitive value null
the primitive value undefined
Everything else is truthy.
Here is the list on the language specification.
In your case cache[0] returns 0 which as we can see is falsy so it enters recursion. This is why we avoid || for short circuiting in these situations.
You should consider checking directly that the object has that property: number in cache is one such way and another is cache[number] !== undefined.
Your code doesn't work because you can't use the value in cache when it's 0 as 0 || func() asks for the function to be called.
So it always call the second term for 0 and thus makes a stack overflow as the recursion has no end.
A solution would be to change your internal function like this :
function fibnonacci(number) {
if (number in cache) return cache[number];
return cache[number] = fibnonacci(number - 1) + fibonacci(number - 2);
}
As an aside please note the spelling of Fibonacci.
It returns the first true from the left. If no true it returns false. If the expressions resolve to true, in case of a Boolean or a non-zero or non-null or non-undefined value.
Edit:
Yes, the value has to be truthy...not only true.
This question already has answers here:
Is there a better way to do optional function parameters in JavaScript? [duplicate]
(28 answers)
Closed 3 years ago.
Can I declare default parameter like
function myFunc( a, b=0)
{
// b is my optional parameter
}
in JavaScript?
With ES6: This is now part of the language:
function myFunc(a, b = 0) {
// function body
}
Please keep in mind that ES6 checks the values against undefined and not against truthy-ness (so only real undefined values get the default value - falsy values like null will not default).
With ES5:
function myFunc(a,b) {
b = b || 0;
// b will be set either to b or to 0.
}
This works as long as all values you explicitly pass in are truthy.
Values that are not truthy as per MiniGod's comment: null, undefined, 0, false, ''
It's pretty common to see JavaScript libraries to do a bunch of checks on optional inputs before the function actually starts.
Update
With ES6, this is possible in exactly the manner you have described; a detailed description can be found in the documentation.
Old answer
Default parameters in JavaScript can be implemented in mainly two ways:
function myfunc(a, b)
{
// use this if you specifically want to know if b was passed
if (b === undefined) {
// b was not passed
}
// use this if you know that a truthy value comparison will be enough
if (b) {
// b was passed and has truthy value
} else {
// b was not passed or has falsy value
}
// use this to set b to a default value (using truthy comparison)
b = b || "default value";
}
The expression b || "default value" evaluates the value AND existence of b and returns the value of "default value" if b either doesn't exist or is falsy.
Alternative declaration:
function myfunc(a)
{
var b;
// use this to determine whether b was passed or not
if (arguments.length == 1) {
// b was not passed
} else {
b = arguments[1]; // take second argument
}
}
The special "array" arguments is available inside the function; it contains all the arguments, starting from index 0 to N - 1 (where N is the number of arguments passed).
This is typically used to support an unknown number of optional parameters (of the same type); however, stating the expected arguments is preferred!
Further considerations
Although undefined is not writable since ES5, some browsers are known to not enforce this. There are two alternatives you could use if you're worried about this:
b === void 0;
typeof b === 'undefined'; // also works for undeclared variables
I need a function:
function isSame(a, b){
}
In which, if a and b are the same, it returns true.
, I tried return a === b, but I found that [] === [] will return false.
Some results that I expect this function can gave:
isSame(3.14, 3.14); // true
isSame("hello", "hello"); // true
isSame([], []); // true
isSame([1, 2], [1, 2]); // true
isSame({ a : 1, b : 2}, {a : 1, b : 2}); //true
isSame([1, {a:1}], [1, {a:1}]); //true
You could embed Underscore.js and use _.isEqual(obj1, obj2).
The function works for arbitrary objects and uses whatever is the most efficient way to test the given objects for equality.
the best way to do that is to use a JSON serializer. serialize both to string and compare the string.
There are some examples, adapted from scheme, on Crockford's site. Specifically, check out:
function isEqual(s1, s2) {
return isAtom(s1) && isAtom(s2) ? isEqan(s1, s2) :
isAtom(s1) || isAtom(s2) ? false :
isEqlist(s1, s2);
}
It can all be found here:
http://javascript.crockford.com/little.js
Here is a working example:
http://jsfiddle.net/FhGpd/
Update:
Just wrote some test cases based on the OP. Turns out I needed to modify the sub1 function to check <= 0 not === 0 otherwise isEqual(3.14, 3.14) blew the stack. Also, isEqual does not work for object comparison, so you are on your own there. However, if you follow the examples on Crockford's site you will see how easy and fun it is to write recursive methods that could be used to check for object equality.
Here is something that can work:
function isSame(obj1, obj2, prefer){
// Optional parameter prefer allows to only check for object keys and not both keys and values of an object
var obj_prefer = prefer || "both";
function checkArray(arr1, arr2){
for(var i = 0, j = obj1.length; i<j; i++){
if(obj1[i] !== obj2[i]){return false;}
}
return true;
}
function checkValues(obj_1, obj_2){
for(var prop in obj_1){
if(typeof obj_1[prop] === "function"){ // converting functions to string so that they can be matched
obj_1[prop] = String(obj_1[prop]);
obj_2[prop] = String(obj_2[prop]);
}
if(obj_1[prop] !== obj_2[prop]){ return false;}
}
return true;
}
// The built in === will check everything except when typeof object is "object"
if ( typeof obj1 === "object"){
// typeof Array is object so this is an alternative
if((typeof obj1.push === "function") && (!obj1.hasOwnProperty('push'))){
return checkArray(obj1, obj2);
}
else{
if( obj_prefer !== "keys"){ // do not check for values if obj_prefer is "keys"
return checkValues(obj1, obj2);
}
var keys_1 = Object.keys(obj1);
var keys_2 = Object.keys(obj2);
if(!checkArray(keys_1, keys_2)){return false;}
return true;
}
}
// I thought undefined === undefined will give false but it isn't so you can remove it
if( typeof obj1 === "undefined" && typeof obj2 === "undefined" ){return true}
if(typeof obj1 === "function"){
return String(obj1) === String(obj2);
}
return obj1 === obj2;
}
console.log(isSame(2, 2)); //true
console.log(isSame([1], [1])); // true
Since it converts Functions into Strings to compare them, check out for spaces as that can break things:
var func1 = function(){},
func2 = function(){ }; // function with extra space
isSame(func1, func2); // false
You can check out http://jsfiddle.net/webholik/dwaLN/4/ to try it yourself.
If anyone reading this answer is using Angular.js, you can use angular.equals(obj1,obj2);
According to the docs:
Determines if two objects or two values are equivalent. Supports value
types, regular expressions, arrays and objects.
Two objects or values are considered equivalent if at least one of the
following is true:
Both objects or values pass === comparison.
Both objects or values are of the same type and all of their properties are equal by comparing them with angular.equals.
Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal).
Both values represent the same regular expression (In JavaScript, /abc/ == /abc/ => false. But we consider two regular expressions as
equal when their textual representation matches).
During a property comparison, properties of function type and properties with names that begin with $ are ignored.
Scope and DOMWindow objects are being compared only by identify (===).