Using the ternary operator with string concatenation

Javascript

Javascript Problem Overview


alert("test: "+(1==2)?'hello':'world');

This should show me 'world' on the screen since 1 is not equal to 2.

How come it alerts 'hello'?

Javascript Solutions


Solution 1 - Javascript

Try wrapping your parens around the operation

alert("test: "+ (1 == 2 ? 'hello' : 'world'));

demo: http://jsfiddle.net/hunter/K3PKx/


what this is doing:

alert("test: "+(1==2)?'hello':'world');

is evaluating "test: " + (1==2) as true which outputs 'hello'

Solution 2 - Javascript

Both of the submitted answers are correct, you need to add parentheses. I figured I'd talk briefly about why.

alert("test: "+(1==2)?'hello':'world');

When the parser encounters a statement, it will begin recursively breaking it down into smaller and smaller chunks.

In this case, the first thing it encounters is a function: alert. Immediately the parser will look at alert's arguments, and begin parsing each of them individually. This function only has one argument, "test: "+(1==2)?'hello':'world', making it an easy first step.

At this level we can break our statement down into a series of binary comparisons. Javascript parsers form binary pairs from left-to-right (when order of operation values are the same). Our potential candidates are "test: ", (1==2), 'hello' and 'world' with operators +, ? and :. The parser will first attempt to add "test: " and (1==2). To do this it must first evaluate the statement (1==2) (which evaluates to false). The + operator causes concatenation with strings and forces all primitive variables to attempt to represent themselves as strings as well. false evaluates as the string "false" creating the statement "test: false".

The parser is now ready to evaluate the first part of the ternary: "test: false"?. In Javascript, all non-empty strings evaluate to true, passing the ternary operator's test and picking the first option "hello".

By throwing a few extra parenthesis into the original statement:

alert("test: " + ((1 == 2) ? 'hello' : 'world'));

We tell the parser that we want to evaluate the ternary operator BEFORE concatenation.

Solution 3 - Javascript

Operator Precedence

All operators have what's known as precedence. This is how the language determines the order of operations. Operators with higher precedence will be evaluated before those with lower precedence. Order of operations is what allows expressions to be executed in the correct order.

For example,

1 + 2 * 3 == 1 + 6 == 7

because * has a higher precedence than +. Without precedence, you would get

1 + 2 * 3 == 3 * 3 == 9

+ vs. ?:

In JavaScript, the + operator has higher precedence than the ?: operator. This means that concatenation will take place before the condition in a ternary is evaluated. This can lead to some strange results.

Note: operator associativity and precedence can change between languages. For example, in JavaScript the ?: operator is right associative but it's left associative in PHP. These same comparisons will produce different results between these languages.

var variable, str;

// Equality operators have higher precedence than ?: but lower than +
// so the below expression breaks down like this:
//   ("A" + variable) !== undefined ? "B" : ("C" + "D")
//   "Aundefined" !== undefined ? "B" : "CD"
//   true ? "B" : "CD"
//   "B"
str = "A" + variable !== undefined ? "B" : "C" + "D";
console.log(str);

// For the same reason as above, you get a strange result here.
// Here's how we break it down:
//   ("A" + variable) === undefined ? "B" : ("C" + "D")
//   "Aundefined" === undefined ? "B" : "CD"
//   false ? "B" : "CD"
//   "CD"
str = "A" + variable === undefined ? "B" : "C" + "D";
console.log(str);

This same type of problem will happen no matter what the condition is:

// Check for undefined
var animal;
// Expected: "The animal does not exist", actual: undefined
console.log("The animal " + animal === undefined ? "does not exist" : animal);

// Expected: "The animal undefined", actual: "does not exist"
console.log("The animal " + animal !== undefined ? "does not exist" : animal);

// Check for null
animal = null;
// Expected: "The animal does not exist", actual: null
console.log("The animal " + animal === null ? "does not exist" : animal);

// Expected: "The animal null", actual: "does not exist"
console.log("The animal " + animal !== null ? "does not exist" : animal);

// Check for property
animal = {};
// Expected: "This animal doesn't have a type", actual: undefined
console.log("The animal " + animal.hasOwnProperty('type') ? animal.type : "doesn't have a type");

animal.type = 'is a dog';
// Expected: "This animal is a dog", actual: "is a dog"
console.log("The animal " + animal.hasOwnProperty('type') ? animal.type : "doesn't have a type");

Solution

Using these same precedence rules, we know that parentheses (( ... )) have the highest precedence of any other operators. By grouping operations inside of parentheses, those operations will be evaluated first. This works recursively, allowing you to further group operations inside of deeper sets of parentheses.

Given this, you can write your ternary expressions inside of parentheses to get the desired result.

var animal;
console.log( "The animal " + (animal === undefined ? "does not exist" : animal) );
console.log( "The animal " + (animal !== undefined ? "does not exist" : animal) );

animal = null;
console.log( "The animal " + (animal === null ? "does not exist" : animal) );
console.log("The animal " + (animal !== null ? "does not exist" : animal) );

animal = {};
console.log( "The animal " + (animal.hasOwnProperty('type') ? animal.type : "doesn't have a type") );

animal.type = 'is a dog';
console.log( "The animal " + (animal.hasOwnProperty('type') ? animal.type : "doesn't have a type") );

In general, it's a good idea to wrap your condition in parentheses as well. That way you will get the correct value when generating the condition with operators of a lower precedence.

// Assignment without parentheses
var x = 0;
console.log( x += 2 ? 'x is 2' : 'x is not 2' );

// Assignment with parentheses
x = 0;
console.log( (x += 2) ? 'x is 2' : 'x is not 2' );

Solution 4 - Javascript

You need to add some extra parenthesis:

alert("test: " + ((1 == 2) ? "hello" : "world"));

Solution 5 - Javascript

With ES6 you can use Template Literals:

alert(`test: ${1 === 2 ? 'hello' : 'world'}`);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1091856View Question on Stackoverflow
Solution 1 - JavascripthunterView Answer on Stackoverflow
Solution 2 - JavascriptSandy GiffordView Answer on Stackoverflow
Solution 3 - JavascriptMike CluckView Answer on Stackoverflow
Solution 4 - JavascriptJustin NiessnerView Answer on Stackoverflow
Solution 5 - JavascriptDougView Answer on Stackoverflow