202

I have a requirement to apply the ?? C# operator to JavaScript and I don't know how. Consider this in C#:

int i?=null;
int j=i ?? 10;//j is now 10

Now I have this set up in JavaScript:

var options={
       filters:{
          firstName:'abc'
       } 
    };
var filter=options.filters[0]||'';//should get 'abc' here, it doesn't happen
var filter2=options.filters[1]||'';//should get empty string here, because there is only one filter

How do I do it correctly?

Thanks.

EDIT: I spotted half of the problem: I can't use the 'indexer' notation to objects (my_object[0]). Is there a way to bypass it? (I don't know the names of the filters properties beforehand and don't want to iterate over them).

DarkAjax
  • 15,955
  • 11
  • 53
  • 65
Valentin V
  • 24,971
  • 33
  • 103
  • 152

6 Answers6

388

Here’s the JavaScript equivalent:

var i = null;
var j = i || 10; //j is now 10

Note that the logical operator || does not return a boolean value but the first value that can be converted to true.

Additionally use an array of objects instead of one single object:

var options = {
    filters: [
        {
            name: 'firstName',
            value: 'abc'
        }
    ]
};
var filter  = options.filters[0] || '';  // is {name:'firstName', value:'abc'}
var filter2 = options.filters[1] || '';  // is ''

That can be accessed by index.

MojoFilter
  • 12,256
  • 14
  • 53
  • 61
Gumbo
  • 643,351
  • 109
  • 780
  • 844
  • 74
    Note that this doesn't work if 0 is a valid value of i. – jt000 Feb 01 '15 at 17:40
  • 19
    If anything falsey is a potentially valid input (`0`, `false`, empty string), I would do something like this instead: `var j = (i === null) ? 10 : i;` Which will only replace `null`, rather than anything that can be evaluated to false. – DBS Sep 26 '16 at 11:14
  • Is there an equivalent for this in Groovy? – user6123723 Feb 21 '18 at 20:03
  • if `i` is undefined, this will throw an error. seems useless to me thus.. (?) – phil294 Apr 28 '18 at 03:01
  • 3
    @phil294 `var j = (i != null ? i : 10);` should work, because `undefined` is `==` null, so `i != null` is `false` for both `null` and `undefined`. – ToolmakerSteve Nov 02 '19 at 21:40
54

ES2020 Answer

The new Nullish Coalescing Operator, is finally available on JavaScript. However, do take note of the browser support. You may need to use a JavaScript compiler like Babel to convert it into something more backward compatible.

According to the documentation,

The nullish coalescing operator (??) is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.

const options={
  filters:{
    firstName:'abc'
  } 
};
const filter = options.filters[0] ?? '';
const filter2 = options.filters[1] ?? '';

This will ensure that both of your variables will have a fallback value of '' if filters[0] or filters[1] are null, or undefined.

Do take note that the nullish coalescing operator does not return the default value for other types of falsy value such as 0 and ''. If you wish to account for all falsy values, you should be using the OR operator ||.

wentjun
  • 40,384
  • 10
  • 95
  • 107
30

Logical nullish assignment, 2020+ solution

A new operator has been added, ??=. This is equivalent to value = value ?? defaultValue.

||= and &&= are similar, links below.

This checks if left side is undefined or null, short-circuiting if already defined. If not, the left side is assigned the right-side value.

Basic Examples

let a          // undefined
let b = null
let c = false

a ??= true  // true
b ??= true  // true
c ??= true  // false

// Equivalent to
a = a ?? true

Object/Array Examples

let x = ["foo"]
let y = { foo: "fizz" }

x[0] ??= "bar"  // "foo"
x[1] ??= "bar"  // "bar"

y.foo ??= "buzz"  // "fizz"
y.bar ??= "buzz"  // "buzz"

x  // Array [ "foo", "bar" ]
y  // Object { foo: "fizz", bar: "buzz" }

Functional Example

function config(options) {
    options.duration ??= 100
    options.speed ??= 25
    return options
}

config({ duration: 555 })   // { duration: 555, speed: 25 }
config({})                  // { duration: 100, speed: 25 }
config({ duration: null })  // { duration: 100, speed: 25 }

??= Browser Support Sept 2021 - 90%

??= Mozilla Documentation

||= Mozilla Documentation

&&= Mozilla Documentation

Gibolt
  • 42,564
  • 15
  • 187
  • 127
9

TLDR:

int j=i ?? 10; is perfectly fine to use in javascript also. Just replace int with let.

?? vs ||

?? should be preferred to || because it checks only for nulls and undefined.

All The expressions below are true:

(null || 'x') === 'x' ;
(undefined || 'x') === 'x' ;
//Most of the times you don't want the result below
('' || 'x') === 'x'  ;
(0 || 'x') === 'x' ;
(false || 'x') === 'x' ;
//-----

//Using ?? is preferred
(null ?? 'x') === 'x' ;
(undefined ?? 'x') === 'x' ;
//?? works only for null and undefined, which is in general safer
('' ?? 'x') === '' ;
(0 ?? 'x') === 0 ;
(false ?? 'x') === false ;

Asterisk: Check browser compatibility and if you really need to support these other browsers use babel.

Marinos An
  • 9,481
  • 6
  • 63
  • 96
6

I spotted half of the problem: I can't use the 'indexer' notation to objects (my_object[0]). Is there a way to bypass it?

No; an object literal, as the name implies, is an object, and not an array, so you cannot simply retrieve a property based on an index, since there is no specific order of their properties. The only way to retrieve their values is by using the specific name:

var someVar = options.filters.firstName; //Returns 'abc'

Or by iterating over them using the for ... in loop:

for(var p in options.filters) {
    var someVar = options.filters[p]; //Returns the property being iterated
}
LinusGeffarth
  • 27,197
  • 29
  • 120
  • 174
Alex Rozanski
  • 37,815
  • 10
  • 68
  • 69
  • 1
    @LinusGeffarth Note that this funny `for in` JS loop works only on object literals! It fails miserably on JS arrays (gives index) and JS Maps (gives `undefined`). Hope you don't forget that, lest you are in for a debugging surprise! xP – varun Sep 01 '19 at 15:26
2

Destructuring solution

Question content may have changed, so I'll try to answer thoroughly.

Destructuring allows you to pull values out of anything with properties. You can also define default values when null/undefined and name aliases.

const options = {
    filters : {
        firstName : "abc"
    } 
}

const {filters: {firstName = "John", lastName = "Smith"}} = options

// firstName = "abc"
// lastName = "Smith"

NOTE: Capitalization matters

If working with an array, here is how you do it.

In this case, name is extracted from each object in the array, and given its own alias. Since the object might not exist = {} was also added.

const options = {
    filters: [{
        name: "abc",
        value: "lots"
    }]
}

const {filters:[{name : filter1 = "John"} = {}, {name : filter2 = "Smith"} = {}]} = options

// filter1 = "abc"
// filter2 = "Smith"

More Detailed Tutorial

Browser Support 92% July 2020

Gibolt
  • 42,564
  • 15
  • 187
  • 127