Saw an expression. Javascript Error - javascript

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
));

Related

How to declare two actions in ? : format?

My code is as below:
if(existingWishlistItem) {
return wishlistItems.map(wishlistItem =>
wishlistItem.id === wishlistItemToAdd.id
? toast.error('This item is already in your wishlist')
: wishlistItem
)
}
I want this function to check if there are existing wishlist item in the array, then it pop up an error message to user and return back the wishlistItem array. But I find that I just can write one action after the '?', so are there any ways to pop up the message and return back the wishlistItem at the same time?
Thanks for help!
It's possible to do this with the conditional operator, but it's not a good idea. It's hard to read, hard to debug, and easy to get wrong.
Instead, just use an if:
if (existingWishlistItem) {
for (const {id} of wishlistItems) {
if (id === wishlistItemToAdd.id) {
toast.error('This item is already in your wishlist');
break; // I assume the ID values are unique, so you can stop here
// Or: `return wishlistItems;` if you don't need to make a
// copy in this case
}
}
return wishlistItems; // If you don't need to make a copy
// Or: `return wishlistItems.slice()` if you do need to make a copy
}
(Or — again assuming id values are unique — you could use find instead of the for-of loop to find the existing item.)
For completeness, you can use the comma operator to do two things in any expression (including the operands of the conditional operator): (first, second). The comma operator evaluates its left-hand operand, throws away that result, and then evalutes its right-hand operand and takes that value as its result. Applying that to your example:
// DON'T DO THIS
if (existingWishlistItem) {
return wishlistItems.map(wishlistItem =>
wishlistItem.id === wishlistItemToAdd.id
? (toast.error('This item is already in your wishlist'), wishlistItem)
: wishlistItem
);
}
This is not what map or ternaries are for.
Idiomatically, ternaries are used for conditional behavior that does not have side effects. Like return upperCase ? "HELLO" : "hello". This is because complex ternaries are hard to read and so it's hard to tell, at a glance, where the side effect is happening.
Likewise map is for transforming objects in a sequence according to some function. It's best practice for map to have no side effects, because code is easier to read when side-effects are clearly separated from data transformation.
A far more idiomatic implementation of your code would be:
if(existingWishListItem) {
if (wishlistItems.some(x => x.id === wishlistItemToAdd.id) {
toast.error(msg)
}
return wishListItems
}
Your problem is you're using map which populates a new list of data with the same array length. If you want to find an existing item, you just simply use find. For example
if(existingWishlistItem) {
const foundWishlistItem = wishlistItems.find(wishlistItem => wishlistItem.id === wishlistItemToAdd.id)
if(foundWishlistItem) {
toast.error('This item is already in your wishlist')
//TODO: You can return or do whatever after found existing wishlist item
}
return wishlistItems
}
Besides that, if you want to have true/false value instead of finding an existing object, you can use some instead
if(existingWishlistItem) {
const isFoundWishlistItem = wishlistItems.some(wishlistItem => wishlistItem.id === wishlistItemToAdd.id)
if(isFoundWishlistItem) {
toast.error('This item is already in your wishlist')
//TODO: You can return or do whatever after found existing wishlist item
}
return wishlistItems
}
Depends on how you define your action. You can use the || operator, and that way first expression is your alert and second is the value you return:
let x = [1,2,3];
let y = x.map((a) => a%2===0? (alert("XXx") || a) : a+1
);
console.log(y);

JS: Handle with find() property undefined

I have a method which returns a value from an element in the array. Not all the elements have the property I want to return. I would like to do this function with one line using the method find(). I've tried this way to solve it:
getExecsFromTour(tourId){
return this.repInfo.find(el => el.id == tourId ).execs || [];
}
But the elements which don't contain the property execs return an error of undefined.
To solve it, I had to store the result in a local variable:
getExecsFromTour(tourId){
let items = this.repInfo.find(el => el.id == tourId);
return items != undefined ? items.execs : [];
}
But I would like to know if I am missing something and this function can be achieved with one sentence.
You seem to have the general idea, Array.prototype.find will search the array for the first element which, when used as an argument of the callback, will have the callback return a truthy value. If nothing is found, it returns undefined.
Your code should work, but yes, one way to do it in one line (if you want) is to use:
getExecsFromTour(tourId){
return (this.repInfo.find(el => el.id == tourId) || {}).execs || [];
}
If Array.prototype.find returns undefined, the first inner parenthetical expression will be evaluated to empty object, which can attempt (and fail) to access the .execs key without a TypeError, which will also evaluate to undefined, in which case the function returns empty array, which is what your code above does.
EDIT: Someone commented this solution already, lol, but as the comments say, nothing wrong with keeping it multiline (more readable that way).
what about
getExecsFromTour(tourId){
return this.repInfo.find(el => 'execs' in el && el.id == tourId ).execs || [];
}
...
EDITED
var a = [{execs : 1, id:4}, {id:5}];
function getExecsFromTour(tourId, x){
return (x = a.find(el => 'execs' in el && el.id == tourId )) ? x.execs : [];
}
this time at least I ran it couple of times

Javascript - How to place a statement inside expression?

I'm learning javascript and I have a problem I stumbled upon. I have the following javascript code (I'm searching for the name john inside MyArray, which contains class objects):
MyArray.forEach(
(v,i)=>(
v.name == 'john' && return i
)
);
This is not working, because I get the following error for the 3rd line:
SyntaxError: expected expression, got keyword 'return'
My question is, how can I put statement inside an expression?
How can i put statement inside an expression?
You can't, currently. Nor, in that example, do you want to. The return value of the forEach callback isn't used for anything.
See below the horizontal rule for your specific case, but answering the question you actually asked:
On occasion, wanting to do some statement logic and then have an overall expression take the final result of that statement logic does come up. Consequently, there's a proposal for do expressions working its way through the JavaScript proposals process. It's only at Stage 1 of the process, and so it may not progress, or if it progresses the form of it may change, but for now it looks like this:
let x = do { // Using the `do` expression proposal
let tmp = f(); // (currently just Stage 1, do not use!)
tmp * tmp + 1
};
...where x would get the result of that last expression within the {}.
If you really wanted to do that now, you'd probably use an immediately-invoked arrow function:
let x = (() => {
let tmp = f();
return tmp * tmp + 1;
})();
The purpose of do expression proposal is to make that less verbose, when you need to do.
(Please don't mistake this for my offering an opinion on do expressions either way. I'm just noting the proposal exists and is working through the process.)
Your specific case of finding the index of an item:
In your specific case, you're looking for the Array#findIndex function, which returns the index of the entry where the callback returns a truthy value:
const index = MyArray.findIndex(v => v.name == 'john');
(Or you might want Array#find if you want the entry itself instead of its index.)
Note that since that uses a concise arrow function, the return value of the function is the value of the expression forming the function body. We could use a verbose one instead with an explicit return:
const index = MyArray.findIndex(v => { return v.name == 'john'; });
Looks like you want to find the index of the Mr. John? Then use dedicated method findIndex for this:
const john = MyArray.findIndex(
(v,i) => (
v.name == 'john'
)
);
You cant. Just do:
for(const [index, el] of myArray.entries())
if(el.name === "john") return index;
Or more easy:
return myArray.findIndex(el => el.name === "john");
forEach is just a loop of all elements. You can't return anything there like you did. You could use find for example, if you want the object with the name john:
var MyArray = [
{name: 'test', some: 'data'},
{name: 'john', some: 'data'},
];
var obj = MyArray.find(v => v.name == 'john');
console.log(obj);

JS - ternary statement inside Object.keys()

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];
}
}

Typescript/Javascript assign and return one-liner

Many times I ask myself the same question... With all that syntaxes (not always intuitive) to write quite direct code in JS, I was wondering, would someone know about a one-liner for that kind of operation?
var setFeatured = entry => {
entry.isFeatured = true;
return entry
}
SomeCallThatReturnsAPromise.then(entries => entries.map(setFeatured))
To assign a property and return the object in one shot, that I could put in a readable way directly as arg of entries.map
To give a feedback about what was proposed to me, the common answer was to return a result with a OR operator, after an assignation or function call (which returns undefined, null, false, never, well anything that will trigger the part after the OR):
return entry.isFeatured = true || entry
The interest of my question was to know if I could take advantage of a more compact syntax:
SomeCallThatReturnsAPromise()
.then((entries:EntryType[]) => entries
.map(entry => entry.isFeatured = true || entry)
.filter(entry => entry.something == true))
.then((entries:EntryType[]) => someCallThatReturnsNothingButHasToBeDoneThere() || entries)
.then((entries:EntryType[]) => console.log(entries))
would be easier to read than:
SomeCallThatReturnsAPromise
.then((entries:EntryType[]) => entries
.map(entry => {
entry.isFeatured = true;
return entry;
})
.filter(entry => entry.something == true))
.then((entries:EntryType[]) => {
someCallThatReturnsNothingButHasToBeDoneThere();
return entries;
})
.then((entries:EntryType[]) => console.log(entries))
Notes:
1) I try to avoid creating a function for that. My question was motivated by curiosity and just concerns what Vanilla ES6 or 7 syntaxes have to offer.
2) I was answered to use .forEach rather than .map. I design my code with a functional approach (hence the importance of compact callbacks), so .forEachis not necessarily a good choice for me (and apparently it doesn't have advantages over map in terms of performance or memory consumption). A one-line syntax is convenient both when handling promises callbacks or chains of array functions...
3) the returned type when using the OR operator is a union type, EntryType|null. So it breaks the typing for the subsequent calls and implies a type assertion:
SomeCallThatReturnsAPromise()
.then((entries:EntryType[]) => entries
.map(entry => (entry.isFeatured = true || entry) as EntryType)
.filter(entry => entry.something == true))
.then((entries:EntryType[]) => (someCallThatReturnsNothingButHasToBeDoneThere() || entries) as EntryType[])
.then((entries:EntryType[]) => console.log(entries))
That's getting heavier... I still don't know if I'll use that or stick with the two lines including the return statement.
4) That's a reduced example. I know that my first then contains a synchronous call or that my example could be more accurate.
entries.forEach( (entry) => entry.isFeatured = true );
No need to define the function separately.
Furthermore, as your elements are objects and are handled by reference, one can replace map() by forEach(), which removes the necessity to return a value.
(using map() you would end up with two arrays consisting of the same elements, which probably is not, what you need)
You can do what #Sirko wrote but return it like so:
SomeCallThatReturnsAPromise.then(entries => entries.forEach(entry => entry.isFeatured = true) || entries)
There's no need to use map, instead using forEach will give you a "one-liner", but then you want to return the same value that you received, using logical or (||) you can do that.
although #Sirko is right and, in that specific case, forEach has more sense than using map I think the OP was asking a generic question.
So, in general, how do you assign a property and then return the whole object? this is my suggestion:
function mutateObject(element, value) {
return (element.prop = value) && element || element
}
var object = {prop:1}
var returned = mutateObject(object, 2)
console.log(returned)
How does it work? the first part (element.prop = value) assigns the value to the property and return the value to the expression.
If the value returned is falsy, the value of the || clause is returned. If it's truthy the value of the && is returned.
In this case we return the element itself both times to be sure that's the object it will always be returned, no matter what we set in the property.
Another way to write that is (element.prop = value) ? element : element but, with this syntax, it looks more like a typo with the assignment instead of the comparison so I like the other syntax better.
//Example 1
const gimmeSmile = {}
console.log({ ...gimmeSmile, smile: ":)" })
// Example 2
const smiles = [{},{},{}]
.map(obj => ( { ...obj, smile: ":)"} ));
console.log(smiles)

Categories