Unexpected token, expected "," while looping through objects - javascript

I'm trying to map through collection of objects inside an object and access the color item, but i get an error Unexpected token, expected ",". This is how i'm trying to map through. Is this the right way to map objects to retrieve value from colors.
{Object.keys(this.state.lists).map((item, i) =>
(this.state.lists[item].colors).map(item, i) =>
<li key={i}>{this.state.lists[item].colors[item]} </li>
)}
this.state.lists looks like this:
{{id: 1, colors:["red", "blue"]}, {id: 2, colors:["green", "yellow"]}}

You are not passing a callback function to your second map call, .map(item, i). Hence the syntax error. It should instead be something like .map((item, i) => ...).
Here's some cleaned up code that might make sense of this, though I haven't tested if it works with React:
const colors = Object.keys(this.state.lists).map(itemKey => {
return <li key={itemKey}>{this.state.lists[itemKey].colors[0]}</li>
})
And when you render,
<ul>{colors}</ul>

When using ES6 functions, you can omit the () of the parameters, only if you use 1 parameter. What you've done is actually closed your map before you even got to the fat arrow (=>). Your error is saying it doesn't understand the , in map(item, i), since map doesn't accept a second parameter. Here's a bit of a break-down, followed by some optimized code for your problem.
A basic ES6 function is () => {}, where the parameters go between the () braces, and the code goes between the {}.
Here's a basic sum function: (a, b) => { return a+b }. Since this only has one line, and it's the return value, you can omit the {} brackets. i.e., (a, b) => a+b
Here's a hello function: (name) => { return 'hello ' + name }. Since it only has 1 parameter, you can use name => { return 'hello ' + name }. Or even using the above rule: name => 'hello ' + name.
These shortcuts can make code easier to write, but perhaps more difficult to understand. If in doubt, just always keep the () braces to avoid confusion.
const obj = {
1: {id: 1, colors:["red", "blue"]},
2: {id: 2, colors:["green", "yellow"]}
}
for (key in obj) {
const item = obj[key];
item.colors.map((color, i) => {
console.log( `<li key=${item.id}-${i}>${color}</li>`)
// Below lines are commented out because StackOverflow
// does not process JSX tags. Just uncomment and remove
// the console.log above
// return (
// <li key={item.id}-${i}>{color}</li>
// )
});
}
NOTES: Instead of using Object.keys to get an array of keys, I just use a for...in loop to accomplish the same thing.
Documentation
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in

Related

sessionStorage setItem is not storing the updated object [duplicate]

This question is intended as canonical duplicate target for questions about problems that stem from confusing the single-line/expression arrow function body syntax with automatic return with their multi-line/block version.
I have an arrow function to add two numbers, but when I call it, it returns undefined. Why?
const add = (a, b) => {
a + b
}
console.log(add(1, 2)) // expected: 3, actually: undefined
Alternative question:
My React component is supposed to render list items using map, but the list stays empty. Why?
<ul>
{list.map(item => {
<li>
{item.name}
</li>
})}
</ul>
Arrow functions support two different styles of bodies: expressions and blocks.
If a single expression is provided (e.g. a + b) without braces { } around it, that expression is automatically returned:
const add = (a, b) => a + b
If a block enclosed by { } is provided, it works like a regular function body and requires a dedicated return statement to return a value:
const add = (a, b) => {
return a + b
}
Single-expression bodies are often used to write simple functions in a concise way which execute one operation or condition, as in the following examples:
if (users.every(user => user.age >= 18)) { /* ... */ }
const emails = users.map(user => user.email)
const titleCased = string.replace(/\b\w/g, s => s.toUpperCase())
// in the following example, the return value is irrelevant
setTimeout(() => doStuff(1, 2, 3), 1000)
In most other cases, especially if you want to have multiple statements, loops or conditions in your function body, a block is used.
Note that you can have a function body spanning multiple lines even without a block if it is still a single expression, but if you would like to turn it into a block for readability reasons, you must not forget to add return (unless your function isn't supposed to return anything).
Now, this is the reason why your add function is returning undefined - it neither has a single-expression body (the { } make it a block) nor does it have any return statement in its body. So, what happens is that a + b is evaluated, but the result isn't used for anything - it is thrown away and execution continues, reaching the end of the function and returning without any return value since none was given, i.e. returning undefined.
In the React case, the problem is the same. You are embedding the return value of a .map call, which should be an array of further content to render, but because your callback is not returning any value, you are mapping the items to several undefined values and rendering that at the end.
There is another twist here though: you may often need multiple lines in the element(s) that you return from a function like a map callback, but you will find that neither of the following two options looks quite clean:
<ul>
{list.map(item => <li>
{item.name}
</li>)}
</ul>
<ul>
{list.map(item => {
return <li>
{item.name}
</li>
})}
</ul>
Instead, what is usually done is enclosing the expression in parentheses ( ). It is a still a single expression at the end, avoiding the need for an extra return statement, but is a lot nicer to work with:
<ul>
{list.map(item => (
<li>
{item.name}
</li>
))}
</ul>
For more information about arrow functions in general, see here. To read about other differences between arrow functions and regular functions (such as different behavior of this), see here.
Arrow functions return code in these cases:
Case 1: When it's written inline like below
/* notice that this is an implicit return
and we don't need a return statement here as the code is
in the same line after the => */
const add = (a, b) => a + b
console.log(add(1, 2)) // expected: 3
Case 2: When it's written with round brackets () like this
Case 2 - example 1
/* notice the round bracket here.
You will use this when you have a
block of code here unlike case 1
where you had a single line to return */
const add = (a, b) => ( // this is round bracket, no return needed
a + b
// and other blocks of code. Look at the below JSX example for
// for this case
)
console.log(add(1, 2)) // expected: 3
The above example is similar to first case 2 - example 1, but this case is more suitable for a single block of code mostly for JSX like below
Case 2 - example 2
<ul>
{list.map(item => ( // this is round bracket, no return needed
<li>
{item.name}
</li>
)}
</ul>
Case 3: With an explicit return statement when you use curly braces like this
const add3 = (a, b) => { // curly braces + return statement
return a + b;
};
const res3 = add3(1, 2);
console.log(res3); // 3
In your case, notice you're mixing both cases 2 and 3. Meaning, you are using curly braces as defined in case 3 and also not using return keyword then like in case 2 which is the reason it doesn't work.
Code : https://codesandbox.io/s/javascript-forked-ckjg69?file=/src/index.js

syntax explanation for copying object array and add new property

Can someone explain to me why this is the only version I've tried that works for copying state with es6 and adding another field, out of the other ways tried that are listed below? :
const values = [{key: "1", val: "one"}, {key: "2", val: "two"}];
var works = values.map(v => {
return {
...v,
extra: "stuff"
}
})
And all of these produce these errors:
//Unexpected token '...'
var notWorks1 = values.map(v => ...v, extra: "stuff");
// Rest parameter must be last formal parameter
var notWorks2 = values.map(v => {...v, extra: "stuff"})
// Rest parameter must be last formal parameter
var notWorks3 = values.map(v => {
{
...v,
extra: "stuff"
}
})
// unexpected token 'return'
var notWorks4 = values.map(v =>
return {
...v,
extra: "stuff"
}
)
// unexpected 'return'
var notWorks5 = values.map(v => return ...v, extra: "stuff");
I thought the arrow was implicit return (see first 3 attempts).
Is that first way the only syntax that can be used? If there are any others, I'd like to know as I'm trying to practice and learn multiple options.. and I want the most terse, smallest option (one line).
The answer of #Bulent is correct, but I'd like to add some more information:
//Unexpected token '...'
var notWorks1 = values.map(v => ...v, extra: "stuff");
This syntax doesn't make sense. As #Bulent says, the spread syntax (...) can only be used inside object literals ({ ...v }), array literals ([ ...v ], only if v is an array) or function calls (f( ...v ), again only if v is an array).
// Rest parameter must be last formal parameter
var notWorks2 = values.map(v => {...v, extra: "stuff"})
The problem here is that in JavaScript braces ({, }) have two functions: either for object literals, as you are trying to use here, but also for code blocks, for example around the body of a function. In this case JavaScript is expecting the braces to be the body of the arrow function. What needs to be done is change the context so that an expression (which an object literal is) is expected. This can for example be done by putting the object literal in round brackets:
var notWorks2 = values.map(v => ({...v, extra: "stuff"}))
// Rest parameter must be last formal parameter
var notWorks3 = values.map(v => {
{
...v,
extra: "stuff"
}
})
Again the first pair of braces is consider a (function body) block, and inside a block braces are again expected to be another block. This time wrapping the object literal in round brackets wouldn't do anything, because an expression alone does nothing inside a function body. Instead you have to use return.
// unexpected token 'return'
var notWorks4 = values.map(v =>
return {
...v,
extra: "stuff"
}
)
Arrow functions can consist of either an expression (no braces) or a function body block (with braces). A return statement isn't an expression, so this doesn't work. return statements can only be used inside a function body block.
One final point: Avoid using var. It is outdated. Use const or let instead.
spread syntax is used in an array or object. So you have to put it between {} or [].
{} are compiled as if they belong to the arrow function, not an object.
When you put your code in {}, you need to return the result.
In order to use return in arrow function, you need the put it between {}
The same as 4.

Functions work seperately but cannot compose together [duplicate]

This question already has answers here:
Function composition with multiple arguments using reduceRight() - JavaScript
(3 answers)
Closed 2 years ago.
I'm trying a very simple program to understand composing functions in JavaScript.
// This compose comes from web
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
const array = 'array'
const actionCreator = (type, payload) => {
return {
type,
payload
}
};
const log = object => {
console.log(object)
};
// This works
log(actionCreator(array, [1,2,3,4]));
// This does not
compose(log, actionCreator)(array, [1,2,3,4]);
For some reasons, calling log(actionCreator) works, and it logs{type: 'array', payload: [1,2,3,4]}. But calling compose keeps logging{type: 'array', payload: undefined}. And it's not just an array. Regardless of what the second argument after array is, the payload is always undefined, whether it's a string, a number...But somehow it stills knows type, only payload is undefined So is there something I did wrong. It's so simple, it should not have these kinds of bugs. Thanks for reading.
You can adjust your compose() function:
const compose = (...fns) => (...args) =>
fns.reduceRight((v, f, i) => (i !== fns.length - 1) ? f(v) : f(...args), null);
That will use the list of provided arguments for the first function, and then behave as .reduceRight normally behaves for the rest, using the return value of the previous iteration as the input value for the next.
The .reduceRight() is started with null as the input because the first iteration won't use that.
edit — here's a simpler version:
const compose = (...fns) => (...args) => fns.reduceRight((v, f) => f(v), fns.pop()(...args));
That one uses the last function applied to the arguments as the initial value for the .reduceRight().
It´s clear that your compose function expects "x" but you are passing "(array, [1,2,3,4])" so in fact "x" becomes the first param which is "array", but the second param is undefined cause you are not passing it to the initial value of the accumulator

What do parenthesis surrounding brackets in the return statement of an ES6 arrow function do?

For example in redux actions, I've seen in someone's code:
export const updateMessage = text => {
return (dispatch) => {
dispatch(updateChatMessage(text))
}
}
and:
const updateChatMessage = text => ({
type: types.someActionType,
text
})
it seems to function as an imply a return but I thought that was already implied in an arrow functions brackets following the fat arrow.
What do the parenthesis ({...}) do? are they necessary? Is there an alternate way to accomplish the same thing?
when you write myFunction = value => ({prop: value})
it return the object {prop: value}, in this case {} are object delimiter and not 'function delimiter'
const updateChatMessage = text => ({
type: types.someActionType,
text
})
another eg :
when you want to multiply by two each elem of an array you can write :
array.map(elem => {return elem * 2})
or
array.map(elem => elem * 2) //same result
and if you want an eg with () that wrap an object litteral :
let array = [{val: 2},
{val: 4},
{val: 8},
{val: 16}];
let output = array.map( ({val}) => ({val: val*2}) );
console.log(output);
If you wrap the brackets with parenthesis you are making your function return an object literal (thus you don't need the return keyword). If you don't use parenthesis you have to use the return keyword.
As per the arrow function docs,
// Parenthesize the body of a function to return an object literal expression:
params => ({foo: bar})
That means if you want to return an object implicitly you have to wrap it in parentheses.
Without this, code inside braces will be considered as function body and not an object (as you'd want)
Following are equivalent:
params => { return {foo: bar}} // Explicitly return an object (from function body)
params => ({foo: bar}) // Implicitly return an object by wrapping it with parentheses
In the first example, the {} are used to identify multiple lines of code, which is why the return is required in order to obtain something other than undefined.
In the second example, the {} are used to create an object.

Remove function in React with .filter

While building a Todo app, I want to filter out an object out of my array with a remove function. So far I got this.
deleteTask(task) {
let taskList = this.state.tasks;
var newTask = taskList.filter(function(_task) { return _task != task})
this.setState({
tasks: newTask
});
}
Only problem is, the function returns the whole array while using the function.
So the Task argument that should return just an object out of my array returns the whole array instead while in my newTask var.
How can I bind or make this function work?
The array which I am wanting to remove an object from is not located in the same Component, dont know if that matters. But for extra info.
First off, let's see why _task != task doesn't work as you need. Try this:
const a = { x: 10, y: 'hello' };
const b = { x: 10, y: 'hello' };
console.log(
a==b,
a===b,
Object.is(a,b)
);
Suprising, eh? Read this for more details.
Anyway, you should refactor your code to include an id property in your tasks, so that you can compare two tasks with their ids - no need to worry about weird implementations of object comparisons in JavaScript!
This should then work:
deleteTask(taskId) {
this.setState(prevState => ({
tasks: prevState.tasks.filter(task => task.id !== taskId)
}));
}
Equality operator in javascript compares the references of the object. So even if both object have same value, since they point to different instances, == will always return false. So as I see you have two options:
Refactor your code to include a id part and which that to compare two tasks.
Use some library like lodash or underscore for deep comparison

Categories

Resources