syntax explanation for copying object array and add new property - javascript

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.

Related

Javascript: Different behavior with single-argument Array.map() depending on whether I use curly braces or not (aren't both forms allowed?)

I'm experimenting with Array.map() and got this little snippet, which does perform as intended:
let cities = ["Buenos Aires", "Santa Fe", "Mar del Plata", "Mar de las Pampas"];
function urlify(string) {
return string.split(" ").join("-").toLowerCase();
}
function functionalUrl(elements) {
return elements.map( element => urlify(element) );
}
console.log(functionalUrl(cities));
// ['buenos-aires', 'santa-fe', 'mar-del-plata', 'mar-de-las-pampas' ]
However, if I replace
return elements.map( (element) => urlify(element) );
with
return elements.map( (element) => { urlify(element); } );
(i.e., add parentheses and curly braces) it returns
[undefined, undefined, undefined, undefined]
I don't understand such behavior, as the curly braces/parentheses form is supposed to be "correct", and taking them away (I thought?) is just allowed in the specific case of a single-argument function...
What am I missing here?
Thanks!
elements.map( (element) => { urlify(element); } );
When you do the above code, you are creating the function body (assuming there are more lines of code to be included) and it expects the 'return' keyword, but if you are returning a single value (the result of one single computation), in your case
elements.map( (element) => urlify(element) );
then you don't have to specify the return keyword.
So the correct code for the second scenario using braces will be
elements.map( (element) => {return urlify(element)} );
The first one is a shorthand syntax not using the return keyword, also if you just have only one parameter you don't have to wrap the parameter inside the parenthesis.
So the more concise way should be
elements.map( element => urlify(element) );

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.

ES6: Why does my misplacement of the parenthesis still produce same result?

I noticed that I had a type in one of my forEach statements but I still got the same result.
foo = ["bobby", "tommy", "brendan"]
foo.forEach((f => {
console.log(f)
}))
vs.
foo.forEach((f) => {
console.log(f)
})
I'm curious as to why the result would be the same on the first one, which I made the typo on.
An arrow function with one argument can be written in two ways:
f => {
console.log(f)
}
And
(f) => {
console.log(f)
}
So the braces around the argument part are optional if there is only one argument.
And placing braces around a complete expression does not change anything for that expression, this:
f => {
console.log(f)
}
and this
(f => {
console.log(f)
})
or even this
((f => {
console.log(f)
}))
is identical.
Your first code block can be formatted as this for a better understanding:
foo.forEach(
// first argument of forEach
(f => {
console.log(f)
})
// end of argument list of forEach
)
So there are no misplaced braces, you just removed the optional ones around the f and place optional once around the complete expression.
foo.forEach((f => {
console.log(f)
}))
forEach accepts two paramters(first is method/function and second is optional which value to use as this object when executing callback), wrapping it around with () is not required. Although with arrow functions, if you have a single parameter you don't need to explicity wrap it with (), but you need to wrap you arguments if your method has multiple arguments.
You can check this out for further reading on arrow functions and this for forEach in js

Unexpected token, expected "," while looping through objects

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

Javascript: Convert code string and back

Suppose that I have this code:
var cfg = {
config: {
fields: {
name: {
type: 'text'
},
age: {
type: 'number'
}
},
actions: [
{
label: 'Print',
cb: (model) => {
console.log(model);
}
}
]
},
data: {name: 'Jhon', age: 23}
}
And I want to convert it to a string (to let a user edit it) and then convert it back to executable code, any idea in how to achieve this?
I tried with JSON.stringify and JSON.parse but that will of course strip the functions. .toString returns "[object Object]", iterating on the object and call .toString when the values is a string, function or number is a possibility, any other idea?
The Function constructor takes code as string, so does eval and a couple other. However, if in any way avoidable, do not convert code to string and backwards because of security concerns, ability to debug and a lot of other issues you can run into when doing so.
Converting code to a string is slightly annoying, because you need to make sure you don't redeclare variables and everything in the new context is syntactically correct, e.g. note that obj's f property is again named in the declaration, because it is later given to eval which places it in the global scope where it needs a name.
let obj = { f: function f() { let stuff = "hi"; console.log("hi"); } };
let code = obj.f.toString();
console.log(code);
eval(code);
f();
Note that JSON.stringify has an optional replacer parameter which can be used to customize the process.
I will again highly advise to avoid any code to/from string conversions when possible, in all normal cases this is not needed and should not be done.
You can iterate around that object and set it to inputs elements, like this
for (var key in cfg.config.fields) {
console.log(key, cfg.config.fields[key].type);
console.log(key, cfg.data[key],'data');
console.log('<input type="'+cfg.config.fields[key].type+'" value="'+cfg.data[key]+'">')
}
This is the solution I came up with:
const toString = (code) => {
if(Array.isArray(code)){
return code.map(item => toString(item));
}
else if(typeof code == 'object'){
let tmp = {};
Object.keys(code).forEach(key => {
tmp[key] = toString(code[key])
});
return tmp;
}
else{
return code.toString().split('\n').join('').replace(/ +(?= )/gmi, '');
}
};
This will iterate recursively across all the properties in a random JS structure (an object containing objects and arrays) an return the corresponding structure containing strings, you can then use JSON.stringify to get the actual string.
After the user edited it, it is executed with:
eval(`(${string})`);
As noted by other, be careful using eval, it can be dangerous (an interesting article is https://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/)

Categories

Resources