In the book Eloquent JS in the section of recursion, a program was given:
Consider this puzzle: by starting from the number 1 and repeatedly
either adding 5 or multiplying by 3, an infinite amount of new numbers
can be produced. How would you write a function that, given a number,
tries to find a sequence of such additions and multiplications that
produce that number? For example, the number 13 could be reached by
first multiplying by 3 and then adding 5 twice, whereas the number 15
cannot be reached at all.
I have following program which look like checks it, but I don't know how to make it print he sequence.
function tester (value, key) {
if (value == key) {
return 1;
}
else if (value > key) {
return 0;
}
else {
if ( tester(value+5, key) || tester(value*3, key) ) {
return 1;
}
return 0;
}
}
Your version is a little odd to me, returning 1 or 0 rather than true or false. Are you mostly used to a language that conflates booleans with such integers? But it looks like it should work.
I would write it a bit differently. I generally prefer my recursion to count down to smaller inputs. You can write a simple function to test the values like this:
const m3a5 = (n) => n < 1
? false
: n == 1
? true
: m3a5(n - 5) || (n % 3 === 0 && m3a5(n / 3))
console.log(m3a5(13)) //=> true
console.log(m3a5(15)) //=> false
console.log(m3a5(18)) //=> true
This should be entirely equivalent to yours, modulo the boolean/int differences.
With this one, you can can then expand it in a fairly straightforward manner to allow you to capture the steps:
const m3a5 = (n, steps = []) => n < 1
? false
: n == 1
? steps
: m3a5(n - 5, ['+5'].concat(steps))
|| (n % 3 === 0 && m3a5(n / 3, ['*3'].concat(steps)))
console.log(m3a5(13)) //=> ['*3', '+5', '+5']
console.log(m3a5(15)) //=> false
console.log(m3a5(18)) //=> ['*3', '+5, '+5', '+5']
Note that this will show one possible path, not all of them. For instance ['+5', '*3'] is another possible result for m3a5(18), one which you would get by switching the main branch to
: (n % 3 === 0 && m3a5(n / 3, ['*3'].concat(steps)))
|| m3a5(n - 5, ['+5'].concat(steps))
But if you want all the paths, that would be significantly different code.
You could store the sequence and if found the calculation return the sequence.
function tester(key, value = 1, sequence = value) {
if (value > key) {
return false;
}
if (value === key) {
return sequence;
}
return tester(key, value + 5, '(' + sequence + ' + 5)')
|| tester(key, value * 3, sequence + ' * 3');
}
console.log(tester(15));
console.log(tester(11));
console.log(tester(24));
console.log(tester(37));
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 months ago.
Improve this question
I personally love ternary operators, and in my humble opinion, they make complicated expressions very easy to digest. Take this one:
const word = (distance === 0) ? 'a'
: (distance === 1 && diff > 3) ? 'b'
: (distance === 2 && diff > 5 && key.length > 5) ? 'c'
: 'd';
However in our project's ESLINT rules nested ternary operators are forbidden, so I have to get rid of the above.
I'm trying to find out alternatives to this approach. I really don't want to turn it into a huge if / else statement, but don't know if there's any other options.
Your alternatives here are basically:
That if/else you don't want to do
A switch combined with if/else
I tried to come up with a reasonable lookup map option, but it got unreasonable fairly quickly.
I'd go for #1, it's not that big:
if (res.distance == 0) {
word = 'a';
} else if (res.distance == 1 && res.difference > 3) {
word = 'b';
} else if (res.distance == 2 && res.difference > 5 && String(res.key).length > 5) {
word = 'c';
} else {
word = 'd';
}
If all the braces and vertical size bother you, without them it's almost as concise as the conditional operator version:
if (res.distance == 0) word = 'a';
else if (res.distance == 1 && res.difference > 3) word = 'b';
else if (res.distance == 2 && res.difference > 5 && String(res.key).length > 5) word = 'c';
else word = 'd';
(I'm not advocating that, I never advocate leaving off braces or putting the statement following an if on the same line, but others have different style perspectives.)
#2 is, to my mind, more clunky but that's probably more a style comment than anything else:
word = 'd';
switch (res.distance) {
case 0:
word = 'a';
break;
case 1:
if (res.difference > 3) {
word = 'b';
}
break;
case 2:
if (res.difference > 5 && String(res.key).length > 5) {
word = 'c';
}
break;
}
And finally, and I am not advocating this, you can take advantage of the fact that JavaScript's switch is unusual in the B-syntax language family: The case statements can be expressions, and are matched against the switch value in source code order:
switch (true) {
case res.distance == 0:
word = 'a';
break;
case res.distance == 1 && res.difference > 3:
word = 'b';
break;
case res.distance == 2 && res.difference > 5 && String(res.key).length > 5:
word = 'c';
break;
default:
word = 'd';
break;
}
How ugly is that? :-)
To my taste, a carefully structured nested ternary beats all those messy ifs and switches:
const isFoo = res.distance === 0;
const isBar = res.distance === 1 && res.difference > 3;
const isBaz = res.distance === 2 && res.difference > 5 && String(res.key).length > 5;
const word =
isFoo ? 'a' :
isBar ? 'b' :
isBaz ? 'c' :
'd' ;
You could write an immediately invoked function expression to make it a little more readable:
const word = (() => {
if (res.distance === 0) return 'a';
if (res.distance === 1 && res.difference > 3) return 'b';
if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) return 'c';
return 'd';
})();
Link to repl
We can simplify it using basic operators like && and ||
let obj = {}
function checkWord (res) {
return (res.distance === 0) && 'a'
|| (res.distance === 1 && res.difference > 3) && 'b'
|| (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) && 'c'
|| 'd';
}
// case 1 pass
obj.distance = 0
console.log(checkWord(obj))
// case 2 pass
obj.distance = 1
obj.difference = 4
console.log(checkWord(obj))
// case 3 pass
obj.distance = 2
obj.difference = 6
obj.key = [1,2,3,4,5,6]
console.log(checkWord(obj))
// case 4 fail all cases
obj.distance = -1
console.log(checkWord(obj))
If you are looking to use const with a nested ternary expression, you can replace the ternary with a function expression.
const res = { distance: 1, difference: 5 };
const branch = (condition, ifTrue, ifFalse) => condition?ifTrue:ifFalse;
const word = branch(
res.distance === 0, // if
'a', // then
branch( // else
res.distance === 1 && res.difference > 3, // if
'b', // then
branch( // else
res.distance === 2 && res.difference > 5, // if
'c', // then
'd' // else
)
)
);
console.log(word);
or using named parameters via destructuring...
const branch2 = function(branch) {
return branch.if ? branch.then : branch.else;
}
const fizzbuzz = function(num) {
return branch2({
if: num % 3 === 0 && num % 5 === 0,
then: 'fizzbuzz',
else: branch2({
if: num % 3 === 0,
then: 'fizz',
else: branch2({
if: num % 5 === 0,
then: 'buzz',
else: num
})
})
});
}
console.log(
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16].map(
cv => fizzbuzz(cv)
)
);
edit
It may be clearer to model it after the python if expression like this:
const res = { distance: 1, difference: 5 };
const maybe = def => ({
if: expr => {
if (expr) {
return { else: () => def };
} else {
return { else: els => els };
}
}
});
const word = maybe('a').if(res.distance === 0).else(
maybe('b').if(res.distance === 1 && res.difference > 3).else(
maybe('c').if(res.distance === 2 && res.difference > 5).else('d')
)
);
console.log(word);
edit
Another edit to remove the nested if/else branches:
const res = { distance: 1, difference: 5 };
const makeResolvedValue = def => {
const elseProp = () => def;
return function value() {
return {
if: () => ({ else: elseProp, value })
};
}
};
const value = def => ({
if: expr => {
if (expr) {
return { else: () => def, value: makeResolvedValue(def) };
} else {
return { else: els => els, value };
}
}
});
// with branching if needed
const word = value('a').if(res.distance === 0)
.else(value('b').if(res.distance === 1 && res.difference > 3)
.else(value('c').if(res.distance === 2 && res.difference > 5)
.else('d')
)
);
console.log(word)
// implicit else option for clarity
const word2 = value('a').if(res.distance === 0)
.value('b').if(res.distance === 1 && res.difference > 3)
.value('c').if(res.distance === 2 && res.difference > 5)
.else('d');
console.log(word2);
If all your truthy conditions evaluate to truthy values (so the value between the question mark and the semicolon evaluates to true if coerced to boolean...) you could make your ternary expressions return false as the falsy expression. Then you could chain them with the bitwise or (||) operator to test the next condition, until the last one where you return the default value.
In the example below, the "condsXXX" array represent the result of evaluating the conditions. "conds3rd" simulates the 3rd condition is true and "condsNone" simulates no condition is true. In a real life code, you'd have the conditions "inlined" in the assignment expression:
var conds3rd = [false, false, true];
var condsNone = [false, false, false];
var val3rd = (conds3rd[0] ? 1 : false) ||
(conds3rd[1] ? 2 : false) ||
(conds3rd[2] ? 3 : 4);
var valNone = (condsNone[0] ? 1 : false) ||
(condsNone[1] ? 2 : false) ||
(condsNone[2] ? 3 : 4);
alert(val3rd);
alert(valNone);
Your example could end up like below:
word = ((res.distance === 0) ? 'a' : false) ||
((res.distance === 1 && res.difference > 3) ? 'b' : false) ||
((res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : 'd';
As a side note, I don't feel it's a good looking code, but it is quite close to using the pure ternary operator like you aspire to do...
word = (res.distance === 0) ? 'a'
: (res.distance === 1 && res.difference > 3) ? 'b'
: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
: 'd';
This is an older question, but this is how I would do it... I would start with the default case and then change the variable or pass it unchanged as desired.
var word = 'd';
word = (res.distance === 0) ? 'a' : word;
word = (res.distance === 1 && res.difference > 3) ? 'b' : word
word = (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : word;
Sometimes we have (or just love) to use one-line expressions or variable definitions.
So, we can use a combination of destructive assignments with the ternary operator. For example,
was:
const a = props.a ? props.a : cond2 ? 'val2.0' : 'val2.1' ;
let's update to:
const { a = cond2 ? 'val2.0' : 'val2.1' } = props;
It even remains relatively well readable.
I personally love using ternary expressions for one liners.
Although, I have to agree that nesting ternary expressions can lead to sketchy code.
I started playing with the Object constructor recently to write clearer code:
let param: "one" | "two" | "three";
// Before
let before: number = param === "one" ? 1 : param === "two" ? 2 : 3;
// After
let after: number = Object({
one: 1,
two: 2,
three: 3
})[param];
Real life example:
const opacity =
Platform.OS === "android"
? 1
: Object({
disabled: 0.3,
pressed: 0.7,
default: 1,
})[(disabled && "disabled") || (pressed && "pressed") || "default"];
If you use lodash you can use _.cond
Point free version with lodash/fp:
const getWord = _.cond([
[_.flow(_.get('distance'), _.eq(0)), _.constant('a')],
[_.flow(_.get('distance'), _.eq(1)) && _.flow(_.get('difference'), _.gt(3)), _.constant('b')],
[
_.flow(_.get('distance'), _.eq(2))
&& _.flow(_.get('difference'), _.gt(5))
&& _.flow(_.get('key'), _.toString, _.gt(5)),
_.constant('c'),
],
[_.stubTrue, _.constant('d')],
]);
If you're in the mood for something a little less readable... this might be for you. Write a general function to take an array of conditions (in the order you'd write your if/else) and an array of assignment values. Use .indexOf() to find the first truth in your conditions, and return the assignment array value at that index. Order is critical, conditions need to match up by index to the assignment you want:
const conditionalAssignment = (conditions, assignmentValues) => assignmentValues[conditions.indexOf(true)];
You can modify to handle truthy instead of struct true, and beware the undefined return if indexOf is -1
I faced this too recently and a google search led me here, and I want to share something I discovered recently regarding this:
a && b || c
is almost the same thing as
a ? b : c
as long as b is truthy. If b isn't truthy, you can work around it by using
!a && c || b
if c is truthy.
The first expression is evaluated as (a && b) || c as && has more priority than ||.
If a is truthy then a && b would evaluate to b if b is truthy, so the expression becomes b || c which evaluates to b if it is truthy, just like a ? b : c would if a is truthy, and if a is not truthy then the expression would evaluate to c as required.
Alternating between the && and || trick and ? and || in the layers of the statement tricks the no-nested-ternary eslint rule, which is pretty neat (although I would not recommend doing so unless there is no other way out).
A quick demonstration:
true ? false ? true : true ? false : true ? true ? true : false : true : true
// which is interpreted as
true ? (false ? true : (true ? false : (true ? (true ? true : false) : true))) : true
// now with the trick in alternate levels
true ? (false && true || (true ? false : (true && (true ? true : false) || true))) : true
// all of these evaluate to false btw
I actually cheated a bit by choosing an example where b is always truthy, but if you are just setting strings then this should work fine as even '0' is ironically truthy.
I've been using a switch(true) statement for these cases. In my opinion this syntax feels slightly more elegant than nested if/else operators
switch (true) {
case condition === true :
//do it
break;
case otherCondition === true && soOn < 100 :
// do that
break;
}
ES6 opens the door to this, a different take on a switch statement.
Object.entries({
['a']: res.distance === 0,
['b']: res.distance === 1 && res.difference > 3,
['c']: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
}).filter(n => n[1] === true)[0][0]
I prefer inlined if-else in ternary expression
This..
let assignment;
if (loggedUser === onboard.Employee)
assignment = AssignmentEnum.Employee;
else if (loggedUser === onboard.CreatedBy)
assignment = AssignmentEnum.Manager;
else
assignment = 0;
..can be shortened to:
const assignment =
loggedUser === onboard.Employee ?
AssignmentEnum.Employee
:loggedUser === onboard.CreatedBy ?
AssignmentEnum.Manager
:
0;
Just add the following ES6 lint rules to allow the above code structure
On "indent", add this rule:
"ignoredNodes": ["ConditionalExpression"]
On "operator-linebreak", add this rule:
{ "overrides": { "?": "after", ":": "ignore" } }
But if you don't want to put the assigned value in their own line, aside from the settings above, add the multiline-ternary to off
"multiline-ternary": "off"
Here's an option that probably accomplishes the same thing, but might be verging on unreadable and liable to break. It's probably slightly less efficient than the ternary, too.
const word = [
{ value: 'a', when: () => distance === 0 },
{ value: 'b', when: () => distance === 1 && diff > 3 },
{ value: 'c', when: () => distance === 2 && diff > 5 && key.length > 5 },
{ value: 'd', when: () => 'otherwise' }, // just has to return something truthy
].find(({ when }) => when()).value
Array.prototype.find evaluates each element in the array until its callback returns a truthy value. Then it stops and returns, so we wouldn't evaluate any more cases than necessary.
We could also do it without a bunch of tiny functions. Slightly easier to read, but potentially less efficient if your calculations are slow or prone to throwing errors.
const word = [
{ value: 'a', when: distance === 0 },
{ value: 'b', when: distance === 1 && diff > 3 },
{ value: 'c', when: distance === 2 && diff > 5 && key.length > 5 },
{ value: 'd', when: 'otherwise' }, // just has to be something truthy
].find(({ when }) => when).value
But honestly, for shorter ternaries, I'd probably just go with an if/else (or remove the lint rule if you can get the rest of your team to agree).
I think some of the answers here kinda missed the point why the OP want to use ternary operator. Many prefers ternary expression as it allows them to DRY the assignment operations, not just because they can inline the returned value on same line as the condition and make the code have a lookup-like operation
If you deemed the switch(true) { is not ugly, I wish passing true is optional in javascript, either switch() { or switch {, then you can certainly DRY your assignment operations with switch:
const word =
(() => {
switch (true) {
case res.distance == 0:
return 'a';
case res.distance == 1 && res.difference > 3:
return 'b';
case res.distance == 2 && res.difference > 5 && String(res.key).length > 5:
return 'c';
default:
return 'd';
}
})();
Beside if-else and switch, you can also try square brackets.
const testName = cond1 ? [cond2 : a : b] : [cond3 ? c : d]
I have this html:
<input type='number' id='length' step='0.1' min='0'; max='5'>Length
and this Javascript
num=document.getElementById('length').value;
if(num==1 || 2 || 3 || 4|| 5){
num='0'+num;
}
My problem is this: while I only want the code inside the brackets to execute if the number from the input is an integer, it also activates if it detects 0.8 or some other decimal. Any Idea why? How do I fix it? Thanks.
To make sure num is a whole number, without having to define all possibilities, use:
if (num % 1 == 0)
Why:
num==1 || 2 || 3 || 4|| 5
equals to:
(num==1) || 2 || 3 || 4|| 5
so if num is "1" (always a string type), the expression returns true, otherwise 2 (also a truthy value), eventually your if statement always succeeds.
How to fix:
// implicitly converts the string type into number type before comparison
// returns true if it is an integer-like string
num == Math.floor(num)
So you could do it like this:
if (num == Math.floor(num) && num > 0 && num < 6) {
// an integer-like string that meets the requirement [1, 5]
}
But remember, the num is still string type now. If you want a number, do:
num = +num
You should do
if (num == 1 || num == 2 || num == 3 || num == 4 || num == 5)
WRONG - otherwise it will compare 2 with 2 and says it's true for the 4 last 'if' parameters.
CORRECTO - any number in JS is considered as true.
You have to edit the "If" loop:
if (num == 1 || num == 2 || num == 3 || num == 4 || num == 5)
I was wondering if there was a quick way to test the equality of more than two values in js. Something similar to (= 6 6 6).
In the console, I tried things like...
1 == 1 == 1 == 1
true
2 == 2 == 2 == 2
false
0 == 0 == 0
false
0 == 0 == 0 == 0
true
...which was amusing, but also puzzling.
Is there a quick way of doing this in js?
Thanks.
The reason you got unexpected behavior is because we need to adjust your expectations in js a bit ;) 2 == 2 == 2 == 2 does 3 comparisons, all from left to right. The first comparison is the leftmost 2 == 2, which evaluates to true. After that we get the result of the first comparison being compared to (what is in this case) the 3rd 2. Ie, true === 2, which is false. And finally, we get false === 2, which is also false.
It might help to visualize it as such:
(((2 == 2) == 2) == 2)
I think in general a === b && b === c might be what you're looking for.
EDIT: Ah, and sorry I keep switching out the == for ===. It's just habit. And it's a habit I'd recommend. the === operator doesn't do type casting, so it evaluates the value proper, not a casted version of the value.
It's because true == 1 but true != 2
You can try:
function isEquals() {
var flag = true;
for(var i=1; i<arguments.length; i++) flag = flag && (arguments[i] == arguments[0]);
return flag;
}
isEquals(2,2,2); // true
or:
function isEquals() {
var ar = arguments;
return Array.prototype.every.call(arguments, function(a){return a==ar[0];});
}
Yes you can, but you need to use the "Logical Operators" like the && or || to check more than 1 statement like (x<1 && y>0).
You can use this as a quick easy reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators
If you have more than three values, it might be more convenient to create a function for use on an array:
function allEqual(arr) {
return arr.every(function (x, i) {
return i === 0 || x === arr[i - 1];
});
}
allEqual([1, 1, 1])
ES6:
function allEqual(...arr) {
return arr.every((x, i) => i === 0 || x === arr[i - 1]);
}
allEqual(1, 1, 1)
As an addition to #vp_arth's answer you could even add a method to the Array prototype
Array.prototype.isHomogeneous = function(){
return Array.prototype.every.call(this, function(c,i,a){ return c === a[0];})
}
So you could do
[1,2,3].isHomogeneous() = false
[1,1,1].isHomogeneous() = true
What is the difference between these various uses of the "&&" logical operator?
From Oliver Steele's Functional.js library.
Line 4, "args.length && arg":
0 Function.prototype.partial = function(){
1 var fn = this, args = Array.prototype.slice.call(arguments);
2 return function(){
3 var arg = 0;
4 for ( var i = 0; i < args.length && arg < arguments.length; i++ )
5 if ( args[i] === undefined )
6 args[i] = arguments[arg++];
7 return fn.apply(this, args);
8 };
9 };
From bootstrap.js.
Line 11 below, "'hover' && this.$element":
1 var Carousel = function (element, options) {
2 this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this))
3 this.$indicators = this.$element.find('.carousel-indicators')
4 this.options = options
5 this.paused =
6 this.sliding =
7 this.interval =
8 this.$active =
9 this.$items = null
10
11 this.options.pause == 'hover' && this.$element
12 .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
13 .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
14 }
Also why not just use the + arithmetic operator in the first example?
Here is yet another example that I'm having trouble grokking, from the same Carousel section of bootstrap.js:
this.options.interval
&& !this.paused
&& (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
Well, it's the same operator everywhere, but being used for different purposes by the programmer.
The first code example is not doing args.length && arg at all, but is rather doing the equivalent of (i < args.length) && (arg < arguments.length) because && has lower precedence than <. It means, "loop until either i is at or surpasses the argument count or arg is at or surpasses the argument count". You can see that + is therefore not equivalent in this context.
The other code examples are all exploiting the fact that the logical-and operator (&&) "short-circuits" when its left-hand operand is false, i.e. it only evaluates the expression on the right if the one on the left is true.
So, this.options.pause == 'hover' && this.$element.on(... is a short-hand way of attaching the event listeners only if the pause option is set to 'hover' (because if it's not, the left-hand expression will be false, and the on functions won't be called).
Similarly, the final example you found only calls setInterval (and assigns the result to this.interval) if the desired interval is "true" (I imagine generally non-zero here, but also not undefined, etc.) and the carousel is not paused.