JS - ternary statement inside Object.keys() - javascript

When I put a ternary statement or an if statement inside the Object.keys() like bellow:
Object.keys(date).forEach((index) => {
dates[date[index].id] !== undefined ?
dates[date[index].id] =
[...dates[dates[date[index].id], dates[date[index].name]
: null;
});
I get this error from the Linter:
Expected an assignment or function call and instead saw an expression. (no-unused-expressions)
When I use a regular if statement, I get this error,
Parsing error: Unexpected token (Fatal)
Why?

You've duplicated dates in and got some missing brackets in the assignment:
dates[date[index].id] = [...dates[dates[date[index].id], dates[date[index].name]
Which should be:
dates[date[index].id] = [..dates[date[index].id], dates[date[index].name]]
Demo:
Object.keys(date).forEach((index) => dates[date[index].id] !== undefined ?
dates[date[index].id] = [...dates[date[index].id], dates[date[index].name]] : null);

You could use a regular if condition, without a ternary expression, which the linter does not like.
Object.keys(date).forEach((index) => {
if (dates[date[index].id] !== undefined) {
dates[date[index].id] = [
...dates[dates[date[index].id]],
dates[date[index].name]
];
}
});
Basically the tenary is used with an assignment outside of the operator, like
x = a > 4 ? 7: 8;
whereas your assignment takes place inside of the operator.

You just have a few syntax errors in there, probably stemming from the overcomplicated nesting of property accesses. Keep it simple:
for (const index in date) {
const id = date[index].id;
if (dates[id] !== undefined) {
dates[id].push(date[index].name);
// or if you insist:
// dates[id] = [...dates[id], date[index].name];
}
}

Related

How do I check if a specific key is undefined if the object is undefined?

What am I trying to do?
I want to check if a specific key is undefined, but not able to because the higher level of the object is undefined.
What is the code that currently tries to do that?
The structure of the character object is (that's inside another object that exists):
{
...
character: {
...,
archer: {
...,
description: "shoots things"
}
}
}
What I'd like to simply do is this, but results in an error.
if (character.archer.description !== undefined) { ... }
A fix I have implemented, but hoping to optimize is this:
...
if (character !== undefined) {
if (character.archer !== undefined) {
if (character.archer.description !== undefined) {
setDescription(character.archer.description);
}
}
}
...
What do I expect the result to be?
I expect the code to realize that the if statement should run, even if undefined of undefined, that should mean undefined in general.
What is the actual result?
TypeError: Cannot read property 'undefined' of undefined.
What I think the problem could be?
Since character.archer is undefined (for now), I can not proceed to check character.archer.description.
You can use the optional chaining operator that all evergreen browsers today support. You simply prepend ? before the . for every level that could potentially returned undefined (or null, actually, that also works).
Example with your code:
if (character?.archer?.description !== undefined) { ... }
This is exactly what the optional chaining operator is for! It looks like this ?. and it will return the righthand argument if the lefthand is defined, else it will return undefined
useEffect(() => {
const description = character?.archer?.description;
if (description !== undefined) {
setDescription(description);
}
}, []);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining

What does this JS object declaration syntax mean?

I am working through a node.js beginners tutorial and came across this piece of code:
const respondEcho = (req, res) => {
const { input = '' } = querystring.parse(
req.url.split('?').slice(1).join('')
)
}
The syntax of this line const { input = '' } = querystring.parse() is a little confusing to me.
It seems like a declaration of a constant object set equal to a method which returns key value pairs. What I don't understand however is the assignment inside the curly braces - if this was an object declaration then it would be like this rather:
const {input: ''}
The input='' seems like it would be a default value on the object. Could someone explain the meaning of this syntax?
const { foo = '' } = bar
means that you are destructuring the foo property from bar and you are assigning a default value to it incase it is falsy.
It is practically the same as doing:
let foo = bar.foo;
if (foo === undefined) {
foo = '';
}
It is a destructuring assignment with a default value in the case that the value unpacked from the object is undefined.

way to check "value" of undefines [duplicate]

I'll explain by example:
Elvis Operator (?: )
The "Elvis operator" is a shortening
of Java's ternary operator. One
instance of where this is handy is for
returning a 'sensible default' value
if an expression resolves to false or
null. A simple example might look like
this:
def gender = user.male ? "male" : "female" //traditional ternary operator usage
def displayName = user.name ?: "Anonymous" //more compact Elvis operator
Safe Navigation Operator (?.)
The Safe Navigation operator is used
to avoid a NullPointerException.
Typically when you have a reference to
an object you might need to verify
that it is not null before accessing
methods or properties of the object.
To avoid this, the safe navigation
operator will simply return null
instead of throwing an exception, like
so:
def user = User.find( "admin" ) //this might be null if 'admin' does not exist
def streetName = user?.address?.street //streetName will be null if user or user.address is null - no NPE thrown
You can use the logical 'OR' operator in place of the Elvis operator:
For example displayname = user.name || "Anonymous" .
But Javascript currently doesn't have the other functionality. I'd recommend looking at CoffeeScript if you want an alternative syntax. It has some shorthand that is similar to what you are looking for.
For example The Existential Operator
zip = lottery.drawWinner?().address?.zipcode
Function shortcuts
()-> // equivalent to function(){}
Sexy function calling
func 'arg1','arg2' // equivalent to func('arg1','arg2')
There is also multiline comments and classes. Obviously you have to compile this to javascript or insert into the page as <script type='text/coffeescript>' but it adds a lot of functionality :) . Using <script type='text/coffeescript'> is really only intended for development and not production.
I think the following is equivalent to the safe navigation operator, although a bit longer:
var streetName = user && user.address && user.address.street;
streetName will then be either the value of user.address.street or undefined.
If you want it to default to something else you can combine with the above shortcut or to give:
var streetName = (user && user.address && user.address.street) || "Unknown Street";
2020 Update
JavaScript now has equivalents for both the Elvis Operator and the Safe Navigation Operator.
Safe Property Access
The optional chaining operator (?.) is currently a stage 4 ECMAScript proposal. You can use it today with Babel.
// `undefined` if either `a` or `b` are `null`/`undefined`. `a.b.c` otherwise.
const myVariable = a?.b?.c;
The logical AND operator (&&) is the "old", more-verbose way to handle this scenario.
const myVariable = a && a.b && a.b.c;
Providing a Default
The nullish coalescing operator (??) is currently a stage 4 ECMAScript proposal. You can use it today with Babel. It allows you to set a default value if the left-hand side of the operator is a nullary value (null/undefined).
const myVariable = a?.b?.c ?? 'Some other value';
// Evaluates to 'Some other value'
const myVariable2 = null ?? 'Some other value';
// Evaluates to ''
const myVariable3 = '' ?? 'Some other value';
The logical OR operator (||) is an alternative solution with slightly different behavior. It allows you to set a default value if the left-hand side of the operator is falsy. Note that the result of myVariable3 below differs from myVariable3 above.
const myVariable = a?.b?.c || 'Some other value';
// Evaluates to 'Some other value'
const myVariable2 = null || 'Some other value';
// Evaluates to 'Some other value'
const myVariable3 = '' || 'Some other value';
Javascript's logical OR operator is short-circuiting and can replace your "Elvis" operator:
var displayName = user.name || "Anonymous";
However, to my knowledge there's no equivalent to your ?. operator.
I've occasionally found the following idiom useful:
a?.b?.c
can be rewritten as:
((a||{}).b||{}).c
This takes advantage of the fact that getting unknown attributes on an object returns undefined, rather than throwing an exception as it does on null or undefined, so we replace null and undefined with an empty object before navigating.
i think lodash _.get() can help here, as in _.get(user, 'name'), and more complex tasks like _.get(o, 'a[0].b.c', 'default-value')
There is currently a draft spec:
https://github.com/tc39/proposal-optional-chaining
https://tc39.github.io/proposal-optional-chaining/
For now, though, I like to use lodash get(object, path [,defaultValue]) or dlv delve(obj, keypath)
Update (as of Dec 23, 2019):
optional chaining has moved to stage 4
For the former, you can use ||. The Javascript "logical or" operator, rather than simply returning canned true and false values, follows the rule of returning its left argument if it is true, and otherwise evaluating and returning its right argument. When you're only interested in the truth value it works out the same, but it also means that foo || bar || baz returns the leftmost one of foo, bar, or baz that contains a true value.
You won't find one that can distinguish false from null, though, and 0 and empty string are false values, so avoid using the value || default construct where value can legitimately be 0 or "".
Yes, there is! 🍾
Optional chaining is in stage 4 and this enables you to use the user?.address?.street formula.
If you can't wait the release, install #babel/plugin-proposal-optional-chaining and you can use it.
Here are my settings which works for me, or just read Nimmo's article.
// package.json
{
"name": "optional-chaining-test",
"version": "1.0.0",
"main": "index.js",
"devDependencies": {
"#babel/plugin-proposal-optional-chaining": "7.2.0",
"#babel/core": "7.2.0",
"#babel/preset-env": "^7.5.5"
}
...
}
// .babelrc
{
"presets": [
[
"#babel/preset-env",
{
"debug": true
}
]
],
"plugins": [
"#babel/plugin-proposal-optional-chaining"
]
}
// index.js
console.log(user?.address?.street); // it works
Here's a simple elvis operator equivalent:
function elvis(object, path) {
return path ? path.split('.').reduce(function (nestedObject, key) {
return nestedObject && nestedObject[key];
}, object) : object;
}
> var o = { a: { b: 2 }, c: 3 };
> elvis(o)
{ a: { b: 2 }, c: 3 }
> elvis(o, 'a');
{ b: 2 }
> elvis(o, 'a.b');
2
> elvis(o, 'x');
undefined
You can achieve roughly the same effect by saying:
var displayName = user.name || "Anonymous";
UPDATE SEP 2019
Yes, JS now supports this.
Optional chaining is coming soon to v8 read more
This is more commonly known as a null-coalescing operator. Javascript does not have one.
I have a solution for that, tailor it to your own needs, an excerpt from one of my libs:
elvisStructureSeparator: '.',
// An Elvis operator replacement. See:
// http://coffeescript.org/ --> The Existential Operator
// http://fantom.org/doc/docLang/Expressions.html#safeInvoke
//
// The fn parameter has a SPECIAL SYNTAX. E.g.
// some.structure['with a selector like this'].value transforms to
// 'some.structure.with a selector like this.value' as an fn parameter.
//
// Configurable with tulebox.elvisStructureSeparator.
//
// Usage examples:
// tulebox.elvis(scope, 'arbitrary.path.to.a.function', fnParamA, fnParamB, fnParamC);
// tulebox.elvis(this, 'currentNode.favicon.filename');
elvis: function (scope, fn) {
tulebox.dbg('tulebox.elvis(' + scope + ', ' + fn + ', args...)');
var implicitMsg = '....implicit value: undefined ';
if (arguments.length < 2) {
tulebox.dbg(implicitMsg + '(1)');
return undefined;
}
// prepare args
var args = [].slice.call(arguments, 2);
if (scope === null || fn === null || scope === undefined || fn === undefined
|| typeof fn !== 'string') {
tulebox.dbg(implicitMsg + '(2)');
return undefined;
}
// check levels
var levels = fn.split(tulebox.elvisStructureSeparator);
if (levels.length < 1) {
tulebox.dbg(implicitMsg + '(3)');
return undefined;
}
var lastLevel = scope;
for (var i = 0; i < levels.length; i++) {
if (lastLevel[levels[i]] === undefined) {
tulebox.dbg(implicitMsg + '(4)');
return undefined;
}
lastLevel = lastLevel[levels[i]];
}
// real return value
if (typeof lastLevel === 'function') {
var ret = lastLevel.apply(scope, args);
tulebox.dbg('....function value: ' + ret);
return ret;
} else {
tulebox.dbg('....direct value: ' + lastLevel);
return lastLevel;
}
},
works like a charm. Enjoy the less pain!
You could roll your own:
function resolve(objectToGetValueFrom, stringOfDotSeparatedParameters) {
var returnObject = objectToGetValueFrom,
parameters = stringOfDotSeparatedParameters.split('.'),
i,
parameter;
for (i = 0; i < parameters.length; i++) {
parameter = parameters[i];
returnObject = returnObject[parameter];
if (returnObject === undefined) {
break;
}
}
return returnObject;
};
And use it like this:
var result = resolve(obj, 'a.b.c.d');
* result is undefined if any one of a, b, c or d is undefined.
I read this article (https://www.beyondjava.net/elvis-operator-aka-safe-navigation-javascript-typescript) and modified the solution using Proxies.
function safe(obj) {
return new Proxy(obj, {
get: function(target, name) {
const result = target[name];
if (!!result) {
return (result instanceof Object)? safe(result) : result;
}
return safe.nullObj;
},
});
}
safe.nullObj = safe({});
safe.safeGet= function(obj, expression) {
let safeObj = safe(obj);
let safeResult = expression(safeObj);
if (safeResult === safe.nullObj) {
return undefined;
}
return safeResult;
}
You call it like this:
safe.safeGet(example, (x) => x.foo.woo)
The result will be undefined for an expression that encounters null or undefined along its path. You could go wild and modify the Object prototype!
Object.prototype.getSafe = function (expression) {
return safe.safeGet(this, expression);
};
example.getSafe((x) => x.foo.woo);
Jumping in very late, there's a proposal[1] for optional chaining currently at stage 2, with a babel plugin[2] available. It's not currently in any browser I am aware of.
https://github.com/tc39/proposal-optional-chaining
https://www.npmjs.com/package/#babel/plugin-proposal-optional-chaining
This was a problem for me for a long time. I had to come up with a solution that can be easily migrated once we get Elvis operator or something.
This is what I use; works for both arrays and objects
put this in tools.js file or something
// this will create the object/array if null
Object.prototype.__ = function (prop) {
if (this[prop] === undefined)
this[prop] = typeof prop == 'number' ? [] : {}
return this[prop]
};
// this will just check if object/array is null
Object.prototype._ = function (prop) {
return this[prop] === undefined ? {} : this[prop]
};
usage example:
let student = {
classes: [
'math',
'whatev'
],
scores: {
math: 9,
whatev: 20
},
loans: [
200,
{ 'hey': 'sup' },
500,
300,
8000,
3000000
]
}
// use one underscore to test
console.log(student._('classes')._(0)) // math
console.log(student._('classes')._(3)) // {}
console.log(student._('sports')._(3)._('injuries')) // {}
console.log(student._('scores')._('whatev')) // 20
console.log(student._('blabla')._('whatev')) // {}
console.log(student._('loans')._(2)) // 500
console.log(student._('loans')._(1)._('hey')) // sup
console.log(student._('loans')._(6)._('hey')) // {}
// use two underscores to create if null
student.__('loans').__(6)['test'] = 'whatev'
console.log(student.__('loans').__(6).__('test')) // whatev
well I know it makes the code a bit unreadable but it's a simple one liner solution and works great. I hope it helps someone :)
This was an interesting solution for the safe navigation operator using some mixin..
http://jsfiddle.net/avernet/npcmv/
// Assume you have the following data structure
var companies = {
orbeon: {
cfo: "Erik",
cto: "Alex"
}
};
// Extend Underscore.js
_.mixin({
// Safe navigation
attr: function(obj, name) { return obj == null ? obj : obj[name]; },
// So we can chain console.log
log: function(obj) { console.log(obj); }
});
// Shortcut, 'cause I'm lazy
var C = _(companies).chain();
// Simple case: returns Erik
C.attr("orbeon").attr("cfo").log();
// Simple case too, no CEO in Orbeon, returns undefined
C.attr("orbeon").attr("ceo").log();
// IBM unknown, but doesn't lead to an error, returns undefined
C.attr("ibm").attr("ceo").log();
I created a package that makes this a lot easier to use.
NPM jsdig
Github jsdig
You can handle simple things like and object:
const world = {
locations: {
europe: 'Munich',
usa: 'Indianapolis'
}
};
world.dig('locations', 'usa');
// => 'Indianapolis'
world.dig('locations', 'asia', 'japan');
// => 'null'
or a little more complicated:
const germany = () => 'germany';
const world = [0, 1, { location: { europe: germany } }, 3];
world.dig(2, 'location', 'europe') === germany;
world.dig(2, 'location', 'europe')() === 'germany';
?? would work in js which is equivalent to ?: in kotlin
Personally i use
function e(e,expr){try{return eval(expr);}catch(e){return null;}};
and for example safe get:
var a = e(obj,'e.x.y.z.searchedField');

Saw an expression. Javascript Error

So, I have a typical problem Expected an assignment or function call and instead saw an expression. I know that there is a lot of such problems and answers here, but no one did not help me.
Please, give a real help, insted of blocking my question.
Thanks!
Object.keys(CDs).map(key => {
parseInt(key) === additionalInfo.currentQueue[0] // Expected an assignment or function call and instead saw an expression
? CDs[key] += 1
: CDs[key]
})
The warning is telling you that you have an orphaned expression there, one that isn't connected to anything. The ternary there will resolve to the value of CDs[key] or CDs[key] + 1, but the value will be ignored - it's like the 5 in
function x() {
5;
}
If you were intending to map the keys of CDs to a new array, use an arrow function's implicit return instead, by surrounding the expression in parentheses rather than brackets:
const result = Object.keys(CDs).map(key => (
parseInt(key) === additionalInfo.currentQueue[0]
? CDs[key] + 1 // were you intending to mutate the original object as well here?
: CDs[key]
));
If you weren't intending to mutate CDs during this map, you might consider Object.entries instead, to give you the key and the value at once:
const result = Object.entries(CDs).map(([key, value]) => (
parseInt(key) === additionalInfo.currentQueue[0]
? value + 1
: value
));

Expected an assignment or function and instead saw an expression

Below is the piece of code I'm having a problem with. I get the JShint "Expected an assignment or function and instead saw an expression".
function checkVal(inputField) {
( inputField.val() === '' ) ? inputField.prev('.cd-label').removeClass('float') : inputField.prev('.cd-label').addClass('float');
}
});
The warning is telling you that the following line could be a mistake or bug:
( inputField.val() === '' ) ? inputField.prev('.cd-label').removeClass('float') : inputField.prev('.cd-label').addClass('float');
Its an expression using the ternary operator that returns the value after the ? if the expression before it is true, or the value after the : otherwise. So basically, it's like a shorthand if statement that results in an assignment.
To remove the warning, you need to assign it to a variable like this:
var yourVariable = ( inputField.val() === '' ) ? inputField.prev('.cd-label').removeClass('float') : inputField.prev('.cd-label').addClass('float');
However, for your case you probably don't really want to assign this to anything, so you should just use an if statement instead.
You should be using an if block here.
if( inputField.val() === '' ){
inputField.prev('.cd-label').removeClass('float');
}
else{
inputField.prev('.cd-label').addClass('float');
}
The ternary operator (?:) should only be used in a context where it returns a value. Such as:
var x = condition ? 'a' : 'b';

Categories