Optional values from object in JavaScript [duplicate] - javascript

This question already has answers here:
How to use optional chaining in Node.js 12
(5 answers)
Closed 22 days ago.
My intent is to take the value from an object and pass it to other or use a default value if key doesn't exists. They use the operator ||, but it just do not work for me.
When I try to compile the snippet on code section in an online coding platform: https://www.programiz.com/javascript/online-compiler/, it throws throws an error:
Code:
const object = {
"a": 1,
"b": 2
};
console.log({
"a": object?.a || 2,
"b": object?.b || 4,
"c": object?.c || 6
});
Error:
"a": object?.a || 2,
^
SyntaxError: Unexpected token '.'
at wrapSafe (internal/modules/cjs/loader.js:915:16)
at Module._compile (internal/modules/cjs/loader.js:963:27)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47

It looks like whatever JavaScript engine that site is using doesn't support optional chaining (?.). But you don't need optional chaining in your example, object is never null or undefined — the properties on it may not exist, but object itself is never null or undefined. So just remove the ? ("a": object.a || 2,).
const object = {
a: 1,
b: 2,
};
console.log({
a: object.a || 2,
b: object.b || 4,
c: object.c || 6,
});
That said, you might look for other sites (or run things locally). Optional chaining has been in the language for a couple of years now...
Another thing added at the same time as optional chaining is relevant here: ??, which is "nullish coalescing." It's probably what you really want for that code. While || will use the right-hand value if the left-hand value is any falsy value, ?? only uses the right-hand value if the left-hand value is null or undefined, specifically. Here's an example of the difference — note how I changed the value of a from 1 to 0, and note what gets logged after using each operator:
const object = {
a: 0,
b: 2,
};
console.log("Using ||:");
console.log({
a: object.a || 2,
b: object.b || 4,
c: object.c || 6,
});
console.log("Using ??:");
console.log({
a: object.a ?? 2,
b: object.b ?? 4,
c: object.c ?? 6,
});
.as-console-wrapper {
max-height: 100% !important;
}
Notice how object.a || 2 used 2, but object.a ?? 2 used 0 (the value of object.a).
In cases where object might be null or undefined, you might want both optional chaining and nullish coalescing:
function example(object) {
console.log(`Using ${JSON.stringify(object)}:`);
console.log({
a: object?.a ?? 2,
b: object?.b ?? 4,
c: object?.c ?? 6,
});
}
example(null);
example({
a: 0,
b: 2,
});
.as-console-wrapper {
max-height: 100% !important;
}

Might be because of wrong version of JS (?.) is relatively a new thing.
You can go with either
object.a || 2
OR
object.hasOwnProperty('a') ? object.a : 2

It is not the OR operator (||) but the optional chaining operator (?.) the one that produces the error.
The optional chaining operator ?. is new operator that has been introduced recently in the language and has been implemented in 2020 by most browsers. You are probably using an older version of the browser and it does not know about ?.
It means that if a is defined then the value of a?.b is a.b, otherwise it is undefined.
The expression a?.b is the same thing as a ? a.b : undefined. Alternatively it can be written as (a || {}).b.
Multiple operators in the same expression can be replaced by any of the above replacement by using parentheses. a?.b?.c can be written as:
(temp = a ? a.b : undefined) ? temp.c : undefined or
((a || {}).b || {}).c
In this particular situation you don't even need the optional chaining operator because object is defined and object?.a is object.a.
Your code can be written as:
const object = {
"a": 1,
"b": 2
}
console.log({
"a": object.a || 2,
"b": object.b || 4,
"c": object.c || 6
});

I am new to javascript. My intent is to take the value from an object and pass to other OR use a default value if key is inexistent
You may also want to look for nullish coalescing operator (??):
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing
Using || for this kind of purpose may lead to unwanted consequences if the left-side operand is "falsy", but not undefined.
E.g.
const currentValue = 0;
const a = currentValue || 2;
console.log(a);
// => 2
As #mplungjan said,
Perhaps it is not supported on the compiler:
https://caniuse.com/mdn-javascript_operators_optional_chaining
(Note: Caniuse only lists browser's JavaScript support. This is different from Node.js, which Programiz uses.)
I checked that Programiz uses Node.js v12.22.9, which does not support optional chaining (i.e., the ?. syntax). You can verify this by yourself:
// Online Javascript Editor for free
// Write, Edit and Run your Javascript code using JS Online Compiler
console.log(process);
node /tmp/Y4wzKlcLGW.js
process {
version: 'v12.22.9',
versions: {
node: '12.22.9',
v8: '7.8.279.23-node.56',
...
To have your environment supports optional chaining, you will need to use Node v14 or above (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining), OR set up a transpiler like Babel.
So, instead of running your JS files directly, you can first transpile them using Babe (which transforms your source files that may use the latest syntaxes, to syntaxes that your Node.js can understand), then run the transpiled files using Node.js as usual.

I recommend Object Destructuring here (this works for arrays and objects) --- it's one of the most useful bits of JS and I use it daily at work. destructure two objects inside curly braces and the later object's values will overwrite the earlier object's values.
here's a code snippet below -
const object = {
a: 1,
b: 2,
};
console.log({
...{
a: 2,
b: 4,
c: 6,
},
...object,
});
// output: {a: 1, b: 2, c: 6}
To make this more readable:
const object = {
a: 1,
b: 2,
};
const objectDefaults = {
a: 2,
b: 4,
c: 6,
};
console.log({
// 'object' will overwrite 'objectDefaults' here (only where the keys match)
// otherwise the values from 'objectDefaults' are preserved
...objectDefaults,
...object,
});
// output: {a: 1, b: 2, c: 6}
But you have to put the objects in the right order. This would never work:
{
...object,
...objectDefaults,
}

Related

how to return almost similar object without repeating the entire object again

I have a javascript object something like below:
something = {
a: 1,
b: 2,
c: 3,
}
In my “if” condition, I want to return the above object but in my “else” condition, I want to return the same object but without the third property. 
c: 3
  Is there a way I can do that without repeating  or writing the object again in the else condition and excluding the third property (c:3)
Hope I made the question clear. The reason I am doing this is because my if condition is very big and I don't want to write it again in the else block since it will be repeating the entire if logic but with one less property. Please let me know If I was unable to explain my problem
Make the base object without c, then add the c property in the if:
const base = {
a: 1,
b: 2,
};
if (cond) {
return { ...base, c: 3 };
} else {
return base;
}
Another option, by using && to conditionally spread in the c property:
return {
a: 1,
b: 2,
...(cond && { c: 3 })
};
You can use delete operator also as an alternative.
const something = {
a: 1,
b: 2,
c: 3,
};
if (condition) {
return something;
} else {
delete something.c;
return something;
}

Array filtering in nested object using ramda

Let's assume that we have the following object:
const sample = {
foo: {
tags: [
'aaa', 'bbb'
],
a: 1,
b: 10
},
bar: {
tags: [
'ccc', 'ddd'
],
a: 11,
b: 100
}
}
How can one remove a specific tag value from object sample using ramda?
I have done this
/// Remove tag named 'aaa'
R.map(v => R.assoc('tags', R.without('aaa', v.tags), v), sample)
which achieves the desired result but how can I eliminate the lamda (and the closure created) inside map ?
You could use evolve instead of assoc. assoc expects a property and plain value to set on provided object, whereas evolves expects a property and function producing the new value (although in a slightly other syntax).
R.map(R.evolve({tags: R.without('aaa')}), sample)
You can R.evolve each object, and use R.without to transform the value of tags:
const { map, evolve, without } = R
const fn = map(evolve({
tags: without('aaa')
}))
const sample = {"foo":{"tags":["aaa","bbb"],"a":1,"b":10},"bar":{"tags":["ccc","ddd"],"a":11,"b":100}}
const result = fn(sample)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>

How to use a destructuring assignment inside an object

Is it possible to use a destructuring assignment inside an object?
This works
const test = {a: 'hey', b: 'hello'}
const {a,b} = test;
const destruct = {
a,
b
};
Would like to do this
const test = {a: 'hey', b: 'hello'}
// something like this
const destruct = {
{a,b}: test
};
const destruct = {
{a}: test,
{b}: test
};
If I understand correctly, it seems the spread syntax is a good fit for what you need.
The spread syntax "..." allows you to "spread" the key/value pairs from a source object (ie test) to a target object (ie destruct):
const test = {
a: 'hey',
b: 'hello',
c: 'goodbye'
}
const destruct = {
// {a,b}: test <-- invalid syntax
...test // equivalent using the "spread" syntax
};
console.log(destruct)
Additionally, if you wanted to select a subset of keys from a source object and spread those into a target object then this can be achieved by the following:
const test = {
a: 'hey',
b: 'hello',
c: 'goodbye'
}
/* Spread subset of keys from source object to target object */
const welcomeOnly = {
...({ a, b } = test, { a, b })
}
console.log('exclude goodbye, show welcomes only:', welcomeOnly);
The second example works by destructing the source object (ie test) into an object, with the subset of keys that we want (a and b).
In the scope of that expression (ie everything between the ( and )), these keys are accessible as local variables. We take advantage of this, and pass those to a new object (ie { a, b }). Because the new object is declared after the ,, it is returned as the result of the expression.
If you are trying to take a subset of properties you can use the rest operator
const test = {
a: 'hey',
b: 'hello',
c: 'goodbye'
};
const { c, ...destruct } = test;
console.log(destruct);
This assigns c to a const and the the left over properties are assigned to the const destruct. List all the unwanted properties first and then the left over properties are caught with the rest operator.
Works with arrays as well.
const test = ['hey', 'hello', 'goodbye'];
const [ first, ...rest ] = test;
console.log(rest);
You can try to work like this for destructuring arrays!
let abc = {
a: 'hello',
b: 'hey',
c: 'hi, there!'
}
let {a: x, b:y, c:z} = abc;
console.log(x,y,z)
// "hello"
"hey"
"hi, there!"

lodash - how to merge identical object and pushes their primitive value into the respective array

I have an array of identical objects like this. I want to merge them into one object, where as only primitive values will be "stacked" into an array while Objects will be traversed and arrays will be concatenated.
I would prefer a functional solution (i can manage fine with for loops,...)
const x = [
{
a: 1,
b: 'hi',
c: { d: 1 },
e:[1,2]
},
{
a: 2,
b: 'there',
c: { d: 2 },
e:[3,4]
}
];
const result = {
a:[1,2],
b:['hi','there'],
c:{
d:[1,2]
},
e:[1,2,3,4]
}
In any case hints to which lodash methods to use/chain would be enough for me to puzzle this out. Currently I'm stuck...
Use _.mergeWith() with a customizer method to handle arrays concatenation and primitives. Since _.mergeWith() is recursive, it will handle nested objects.
const x = [{"a":1,"b":"hi","c":{"d":1},"e":[1,2]},{"a":2,"b":"there","c":{"d":2},"e":[3,4]}];
const result = _.mergeWith({}, ...x, (ov, sv) => {
if(Array.isArray(ov)) return ov.concat(sv);
if(ov !== undefined && !_.isObject(ov)) return [ov, sv];
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

Chai.js: Object contains/includes

Chai has an include method. I want to test to see if an object contains another object. For example:
var origin = {
name: "John",
otherObj: {
title: "Example"
}
}
I want to use Chai to test if this object contains the following (which it does)
var match = {
otherObj: {
title: "Example"
}
}
Doing this does not appear to work:
origin.should.include(match)
Hei, just published chai-subset. Check this out: https://www.npmjs.org/package/chai-subset
This should work for you)
var chai = require('chai');
var chaiSubset = require('chai-subset');
chai.use(chaiSubset);
var obj = {
a: 'b',
c: 'd',
e: {
foo: 'bar',
baz: {
qux: 'quux'
}
}
};
expect(obj).to.containSubset({
e: {
foo: 'bar',
baz: {
qux: 'quux'
}
}
});
The include and contain assertions can be used as either property based language chains or as methods to assert the inclusion of an object in an array or a substring in a string. When used as language chains, they toggle the contain flag for the keys assertion. [emphasis mine]
So if you're invoking include on an object (not an array or a string), then it only serves to toggle the contain flag for the keys assertion. By the looks of your example, testing for deep equality would make more sense, possibly checking for the key first.
origins.should.include.keys("otherObj");
origins.otherObj.should.deep.equal(match.otherObj);
Actually, now I browse the other examples, you would probably be happiest with this :
origins.should.have.deep.property("otherObj", match.otherObj)
In chai 4.2.0, for example you can use deep include
chaijs doc examples:
// Target array deeply (but not strictly) includes `{a: 1}`
expect([{a: 1}]).to.deep.include({a: 1});
expect([{a: 1}]).to.not.include({a: 1});
// Target object deeply (but not strictly) includes `x: {a: 1}`
expect({x: {a: 1}}).to.deep.include({x: {a: 1}});
expect({x: {a: 1}}).to.not.include({x: {a: 1}});
If you know the level of the subobject you can simply use:
expect(origin.otherObj).to.include(match.otherObj);
https://www.chaijs.com/api/bdd/
In Chai 1.5.0 you will find handy method
includeDeepMembers
http://chaijs.com/releases/

Categories