spread syntax with map doesn't work - javascript

data.map(obj =>
{person_name: obj.user.name,
...obj})
Why above code failed?
my data look like this
[{user:{name:'hello'},age:1},{user:{name:'world'},age:1,{user:{name:'another_name'},age:1]
I want to 'pull' the obj.user.name out from the nested obj.

Try this:
data.map(obj => ({person_name: obj.user.name,...obj}))
The { at the beginning of the object was interpreted as the beginning of a block instead of an object.
You can read more about this on MDN
EDIT:
As Pawel mentioned, using the spread operator with objects does not work with pure es6 you need to use a specific transform like in this proposal: https://github.com/tc39/proposal-object-rest-spread

You can't spread an object like this (throws SyntaxError at ....
I would write it using Object.assign:
data.map(obj => Object.assign({person_name: obj.user.name}, obj ));

With babel, you could use rest syntax (parameters) ... for getting the rest of the object and store name property directly.
var data = [{ user: { name: 'hello' }, age: 1 }, { user: { name: 'world' }, age: 1 }, { user: { name: 'another_name' }, age: 1 }];
console.log(data.map(({ user, ...obj }) => Object.assign(obj, { person_name: user.name })));
.as-console-wrapper { max-height: 100% !important; top: 0; }

First of all, if you want to create an object using an arrow function, you have to wrap the curly braces in parenthesis:
data.map(obj => ({ foo: 'bar' }));
Otherwise, JS thinks the curly braces are the function body, and you get a syntax error.
And secondly, you can't use the spread operator in object literals. You can only use it when calling a function, using the array literal or when destructuring.

You can use Array.prototype.map() and directly return the desired object:
const data = [{user: {name: 'hello'},age: 1}, {user: {name: 'world'},age: 1},{user: {name: 'another_name'},age: 1}];
const result = data.map(obj => {
return {
person_name: obj.user.name,
age: obj.age
};
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Array.prototype.map argument question in javascript [duplicate]

When returning an object from an arrow function, it seems that it is necessary to use an extra set of {} and a return keyword because of an ambiguity in the grammar.
That means I can’t write p => {foo: "bar"}, but have to write p => { return {foo: "bar"}; }.
If the arrow function returns anything other than an object, the {} and return are unnecessary, e.g.: p => "foo".
p => {foo: "bar"} returns undefined.
A modified p => {"foo": "bar"} throws “SyntaxError: unexpected token: ':'”.
Is there something obvious I am missing?
You must wrap the returning object literal into parentheses. Otherwise curly braces will be considered to denote the function’s body. The following works:
p => ({ foo: 'bar' });
You don't need to wrap any other expression into parentheses:
p => 10;
p => 'foo';
p => true;
p => [1,2,3];
p => null;
p => /^foo$/;
and so on.
Reference: MDN - Returning object literals
You may wonder, why the syntax is valid (but not working as expected):
var func = p => { foo: "bar" }
It's because of JavaScript's label syntax:
So if you transpile the above code to ES5, it should look like:
var func = function (p) {
foo:
"bar"; //obviously no return here!
}
If the body of the arrow function is wrapped in curly braces, it is not implicitly returned. Wrap the object in parentheses. It would look something like this.
p => ({ foo: 'bar' })
By wrapping the body in parens, the function will return { foo: 'bar }.
Hopefully, that solves your problem. If not, I recently wrote an article about Arrow functions which covers it in more detail. I hope you find it useful. Javascript Arrow Functions
Issue:
When you do are doing:
p => {foo: "bar"}
JavaScript interpreter thinks you are opening a multi-statement code block, and in that block, you have to explicitly mention a return statement.
Solution:
If your arrow function expression has a single statement, then you can use the following syntax:
p => ({foo: "bar", attr2: "some value", "attr3": "syntax choices"})
But if you want to have multiple statements then you can use the following syntax:
p => {return {foo: "bar", attr2: "some value", "attr3": "syntax choices"}}
In above example, first set of curly braces opens a multi-statement code block, and the second set of curly braces is for dynamic objects. In multi-statement code block of arrow function, you have to explicitly use return statements
For more details, check Mozilla Docs for JS Arrow Function Expressions
ES6 Arrow Function returns an Object
the right ways
normal function return an object
const getUser = user => {
// do something
const {name, age} = user;
return { name, age };
};
const user = { name: "xgqfrms", age: 21 };
console.log(getUser(user));
// {name: "xgqfrms", age: 21}
(js expressions)
const getUser = user => ({ name: user.name, age: user.age });
const user = { name: "xgqfrms", age: 21 };
console.log(getUser(user));
// {name: "xgqfrms", age: 21}
explain
refs
https://github.com/lydiahallie/javascript-questions/issues/220
https://mariusschulz.com/blog/returning-object-literals-from-arrow-functions-in-javascript
You can always check this out for more custom solutions:
x => ({}[x.name] = x);

How can I dynamically add a key value pair to an array of objects using map

I know this is a simple question but I'm not understanding why my code behaves the way it does. I'm trying to dynamically add a property to an array of objects using array.map(). I can get my code to work the way I want to and make my tests pass but I have to hard code the key which doesn't make the function flexible/reusable and is sort of a 'hack'.
Example:
// this works but I have to hard code the key 'profession' & not quite how I want it to work
function addKeyValue(arr,key,value) {
return arr.map((obj) => ({...obj, profession: value }))
}
// I don't understand why this doesn't work...
function addKeyValue(arr,key,value) {
return arr.map((obj) => ({...obj, obj[key]: value }))
}
// or this doesn't work either...
function addKeyValue(arr,key,value) {
return arr.map((obj) => ({...obj, obj['key']: value }))
}
// or this...even if I'm already enclosing the key in template strings
// which should effectively set the key as a string which is the reason
// why my first example worked
function addKeyValue(arr,key,value) {
return arr.map((obj) => ({...obj, `${key}`: value }))
}
addKeyValue([{name: 'Moe'}, {name: 'Larry'}, {name: 'Curly'}], 'profession', 'comedian')
// [{name: 'Moe', profession: 'comedian'}, {name: 'Larry', profession: 'comedian'}, {name: 'Curly', profession: 'comedian'}]
I know it's probably a really simple thing I'm overlooking and also not understanding so thanks in advance for everybody's help! : )
In order to use an expression as the key in an object literal, put it in [].
function addKeyValue(arr,key,value) {
return arr.map((obj) => ({...obj, [key]: value }))
}
See Create an object with dynamic property names
You need a computed property name.
function addKeyValue(arr,key,value) {
return arr.map((obj) => ({...obj, [key]: value }));
}
console.log(addKeyValue([{ name: 'Moe' }, { name: 'Larry' }, { name: 'Curly' }], 'profession', 'comedian'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Adding new properties when working with spread objects?

I have some existing code that looks like this (I can't change the function definitions of add_from_address or email.send, only what's inside the body of add_from_address):
function add_from_address(...args) {
args.from = "foo#bar.com";
console.log(args);
email.send(...args).catch((error) => {
console.error("Failed", error);
});
}
This is the output of console.log(args):
[ 'subscription-created',
{ subject: 'hello', user_id: user_id },
from: "foo#bar.com" }];
But I was hoping to achieve this:
[ 'subscription-created',
{ subject: 'hello', user_id: user_id, from: "foo#bar.com" }];
Obviously, I could just manually add the new property to the second element of args as follows:
function add_from_address(...args) {
args[1].from = "foo#bar.com";
console.log(args);
email.send(...args).catch((error) => {
console.error("Failed", error);
});
}
But is there a more elegant way to add properties when working with spread operators?
Sinne the object you want to add the property to is the second argument (args[1]), you need to add the property to that object, not the array:
function add_from_address(...args) {
args[1] = {...args[1], from: "foo#bar.com"};
email.send(...args).catch((error) => {
console.error("Failed", error);
});
}
That updates your array with a new object, adding (or overwriting) the from property.
Live Example:
function add_from_address(...args) {
args[1] = {...args[1], from: "foo#bar.com"};
console.log(args);
}
add_from_address(
'subscription-created',
{ subject: 'hello', user_id: 42 }
);
In a comment you've asked why not to use:
args[1].from = "foo#bar.com";
The only issue with doing that is that it modifies the object that was passed into the method. Example:
function add_from_address1(...args) {
args[1] = {...args[1], from: "foo#bar.com"};
console.log(args);
}
function add_from_address2(...args) {
args[1].from = "foo#bar.com";
console.log(args);
}
const obj1 = { subject: 'hello', user_id: 42 };
add_from_address1('subscription-created', obj1);
console.log(obj1.from); // undefined, because the object doesn't have a `from` property
const obj2 = { subject: 'hello', user_id: 42 };
add_from_address2('subscription-created', obj2);
console.log(obj2.from); // "foo#bar.com", because `add_from_address2` modified it
.as-console-wrapper {
max-height: 100% !important;
}
Note how add_from_address1 didn't modify obj1, but add_from_address2 did modify obj2.
Modifying the object that was passed in may be fine in your case. In general, though, methods should leave the caller's objects alone unless the purpose of the method is to modify the object.
Just as a side note, if you want to you can use formal (named) parameters for the first two and rest for the...rest:
function add_from_address(type, msg, ...rest) {
email.send(type, {...msg, from: "foo#bar.com"}, ...rest)
.catch((error) => {
console.error("Failed", error);
});
}

Can you dynamically deconstruct an object for usage with currying?

I've been playing with functional javascript a bit and had an idea for a util function using deconstructing.
Is it possible using ...rest to pass the names of object keys to later filter out properties?
reading through the ...rest docs I haven't seen any mention of deconstructing.
If not what solution could solve this issue?
const stripObject = attr => ({ ...attr }) => ({ ...attr });
const getUserProps = stripObject(['_id', 'firstName']);
console.log(getUserProps({ _id: 1, firstName: 'foo', lastName: 'bar' }));
/*
I understand right now whats happening is the []
passed is being ignored and its just returning a
function that passing in all the props
{
_id: 1,
firstName: 'foo'
}
*/
Just in case you like to spread stuff you could spread a specially prepared Proxy :)
const stripObject = attrs => obj => ({ ...new Proxy(obj, {
ownKeys() {
return attrs
}
})
});
const getUserProps = stripObject(['_id', 'firstName']);
console.log(getUserProps({
_id: 1,
firstName: 'foo',
lastName: 'bar'
}));
{ ...attr } in parameter position means "get all properties of the passed in object and assign it to a new object assigned to attr". I.e. you are just creating a shallow clone of the object that is passed in.
I.e. these two functions are equivalent except for the cloning part
({...foo}) => foo
foo => foo
So no, what you want is not possible (this way). You cannot declare parameters dynamically.
If you want to pull out specific props, you can do adapt this approach (One-liner to take some properties from object in ES 6) to your requirements:
const stripObject = attr => obj => pick(obj, ...attr);
After learning what I originally isn't possible solution I ended up using was to reduce over the keys initially passed then grab the prop form the object.
const stripObject = keys => obj => {
return keys.reduce((p, c) => (
{ ...p, [c]: obj[c] }
), {});
};
const getUserProps = stripObject(['_id', 'firstName']);
console.log(getUserProps({
_id: 1,
firstName: 'foo',
lastName: 'bar'
}));

Reduce number of operators in an RxJS mapping expression

I've create an Http request to get json data. Inside that json - there is an object which has an array. ( I need that array).
fromDb$ = of({
Result: {
Countries: [{ <--wanted array
ISOCode: 1,
Name: 'aaa'
}, {
ISOCode: 2,
Name: 'bbb'
}]
}
});
But- the data in the array has a different structure than I actually need.
I need to map (name &ISOcode) to (name and value )
This is what I've tried:
Use pluck to extract the inner Array
mergeMap the array object to a stream of objects (using of())
using map to transform each item to a desired structure
using toArray to wrap all to an array ( so I can bind it to a control)
Here is the actual code :
this.data = this.fromDb$.pipe(pluck<PtCountries, Array<Country>>('Result', 'Countries'),
mergeMap(a => from(a)),
map((c: Country) => ({
name: c.Name,
value: c.ISOCode,
})),
toArray());
The code does work and here is the online demo
Question
It looks like I've complicated it much more than it can be ,Is there a better way of doing it?
This line: mergeMap(a => from(a)) does not make a lot of sense. It's almost as if you did [1,2,3].map(v => v). You can just remove it.
To simplify this you basically need to use Array.map inside Observable.map.
Try this:
this.data = this.fromDb$.pipe(pluck<PtCountries, Array<Country>>('Result', 'Countries'),
map((countries: Country[]) => countries.map(country => ({
name: country.Name,
value: country.ISOCode,
}))));
Live demo
this.data = this.fromDb$.pipe(
mergeMap(object => object.Result.Countries),
map(country => ({ name: country.Name, value: country.ISOCode })),
toArray()
);

Categories