Related
I have a data object below.
{
name: "Catherine Myer",
age: 23,
birthday: "august"
}
If in need to pass the data as a prop to a component, BUT would also like to change just the age to: 24. How do i do so?
<NextPage data={author.age = 24}/>
I need the final object to be:
{
name: "Catherine Myer",
age: 24,
birthday: "august"
}
You can do it with spread syntax:
<NextPage data={{...author, age: 24}}/>
Either pass individual prop values by spreading author (see Spread Attributes) and override age with a separate prop, eg
const NextPage = ({ name, age, birthday }) => {
// ...
};
<NextPage {...author} age="24" />
or extend the existing object and provide a new age property
const NextPage = ({ data: { name, age, birthday } }) => {
// ...
};
<NextPage data={{...author, age: 24}} />
You can just use the JS spread syntax to update whichever properties you need.
const author = {
name: "Catherine Myer",
age: 23,
birthday: "august"
};
const data = {
age: 24
};
const updatedAuthor = { ...author, ...data };
Edit: I have no idea what I was thinking to make it this complicated...
If you don't know which property will be overwritten, a simple for in loop can make the update. As a bonus, this scales up if you want to modify the value of more than one property at once.
Alternatively, if you really want to (or if you make the change Mike Kamermans recommended), you can use the JS spread syntax to achieve the same.
const author = {
name: "Catherine Myer",
age: 23,
birthday: "august"
};
const data = {
age: 24
};
// OPTION 1: for in loop
let updatedAuthor = { ...author }; // optionally create a copy to avoid mutating the original object
for(let prop in data) {
updatedAuthor[prop] = data[prop];
}
// OPTION 2: spread syntax
const propToUpdate = Object.keys(update)?.[0];
const updatedAuthor = {
...author,
[propToUpdate]: update[propToUpdate]
}
Let's suppose I have the following object:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
And that I want only the id and fullName.
I will do the following :
const { id, fullName } = user
Easy-peasy, right?
Now let's suppose that I want to do the destructuring based on the value of another variable called fields.
const fields = [ 'id', 'fullName' ]
Now my question is : How can I do destructuring based on an array of keys?
I shamelessly tried the following without success:
let {[{...fields}]} = user and let {[...fields]} = user. Is there any way that this could be done?
Thank you
It's not impossible to destructure with a dynamic key. To prevent the problem of creating dynamic variables (as Ginden mentioned) you need to provide aliases.
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName' ];
const object = {};
const {[fields[0]]: id, [fields[1]]: fullName} = user;
console.log(id); // 42
console.log(fullName); // { firstName: "John", lastName: "Doe" }
To get around the problem of having to define static aliases for dynamic values, you can assign to an object's dynamic properties. In this simple example, this is the same as reverting the whole destructuring, though :)
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName' ];
const object = {};
({[fields[0]]: object[fields[0]], [fields[1]]: object[fields[1]]} = user);
console.log(object.id); // 42
console.log(object.fullName); // { firstName: "John", lastName: "Doe" }
sources:
https://twitter.com/ydkjs/status/699845396084846592
https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20%26%20beyond/ch2.md#not-just-declarations
Paul Kögel's answer is great, but I wanted to give a simpler example for when you need only the value of a dynamic field but don't need to assign it to a dynamic key.
let obj = {x: 3, y: 6};
let dynamicField = 'x';
let {[dynamicField]: value} = obj;
console.log(value);
Short answer: it's impossible and it won't be possible.
Reasoning behind this: it would introduce new dynamically named variables into block scope, effectively being dynamic eval, thus disabling any performance optimization. Dynamic eval that can modify scope in fly was always regarded as extremely dangerous and was removed from ES5 strict mode.
Moreover, it would be a code smell - referencing undefined variables throws ReferenceError, so you would need more boilerplate code to safely handle such dynamic scope.
As discussed before, you can't destruct into dynamically named variables in JavaScript without using eval.
But you can get a subset of the object dynamically, using reduce function as follows:
const destruct = (obj, ...keys) =>
keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
const object = {
color: 'red',
size: 'big',
amount: 10,
};
const subset1 = destruct(object, 'color');
const subset2 = destruct(object, 'color', 'amount', 'size');
console.log(subset1);
console.log(subset2);
You can't destruct without knowing the name of the keys or using an alias for named variables
// you know the name of the keys
const { id, fullName } = user;
// use an alias for named variables
const { [fields[0]]: id, [fields[1]]: fullName } = user;
A solution is to use Array.reduce() to create an object with the dynamic keys like this:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName', 'age' ];
const obj = fields.reduce((acc, k) => ({ ...acc, ...(user.hasOwnProperty(k) && { [k]: user[k] }) }), {});
for(let k in obj) {
console.log(k, obj[k]);
}
I believe that the above answers are intellectual and valid because all are given by pro developers. :). But, I have found a small and effective solution to destructure any objects dynamically. you can destructure them in two ways. But both ways are has done the same action.
Ex:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
using "Object.key", "forEach" and "window" object.
Object.keys(user).forEach(l=>window[l]=user[l]);
Simply using Object. assign method.
Object.assign(window, user)
Output:
console.log(id, displayName, fullName)
// 42 jdoe {firstName: "John", lastName: "Doe"}
Anyway, I am a newbie in JS. So, don't take it as an offense if you found any misleading info in my answer.
I Am trying to push a new element to my nested array but the issue I am facing is It is creating new array every time, I know this is very silly but I tried to google but not got the answer.
What i am doing
Below is my state
const [state, setState] = useState([
{
name: "test",
lname: "lname",
ages: [
{
age: 30
}
]
}
]);
What I am trying to do on click is
const randomAge = () => {
let rg = ((Math.random() * 1000) / 10).toFixed();
setState({ ...state, ages: { ...state.ages, age: rg } });
};
But it is returning very wrong output, shown as below
{
"0": {
"name": "test",
"lname": "lname",
"ages": [
{
"age": 30
}
]
},
"ages": {
"rg": "34"
}
}
Output I want is like below
[
{
"name": "test",
"lname": "lname",
"ages": [
{
"age": 30
},
{
"age": 50
},
{
"age": 60
}
]
}
]
I know I am doing a very silly mistake somewhere, but right now I am not abe to find
I am just trying to push a new random age to my ages array on click of button
Code sandbox link
The current code is mutating the state invariant from arrays to objects.
const randomAge = () => {
let rg = ((Math.random() * 1000) / 10).toFixed();
setState({ ...state, ages: { ...state.ages, age: rg } });
};
If the state array only ever has a single element in it then use a functional state update and create a new array reference with only the single object element in it. Shallow copy the previous state's state[0] element and update the ages property by also shallow copying the array and appending the new object with the age property.
Example:
const randomAge = () => {
const age = (Math.random() * 100).toFixed();
setState((prev) => [
{
...prev[0],
ages: [...prev[0].ages, { age }]
}
]);
};
If you are eventually wanting to handle multiple object elements in the state array then I suggest adding a GUID to each object and pass this to the randomAge callback so it can correctly update the correct state.
Example:
import React, { useState } from "react";
import { nanoid } from "nanoid";
export default function App() {
const [state, setState] = useState([
{
id: nanoid(),
name: "test1",
lname: "lname1",
ages: []
},
{
id: nanoid(),
name: "test2",
lname: "lname2",
ages: []
}
]);
const randomAge = (id) => () => {
const age = (Math.random() * 100).toFixed();
setState((prev) =>
prev.map((el) =>
el.id === id
? {
...el,
ages: el.ages.concat({ age })
}
: el
)
);
};
return (
<div className="App">
{state.map((li, ind) => {
return (
<React.Fragment key={li.id}>
<div>{li.name}</div>
<div>{li.lname}</div>
<br />
{li.ages.map((d) => <div>{d.age}</div>)}
<button onClick={randomAge(li.id)}>Add random age</button>
</React.Fragment>
);
})}
</div>
);
}
i am not sure but, you use array in start state
ages: [ {age: 30} ]
but in setState setState({ ...state, ages: { ...state.ages, age: rg } }); you use a object instead of Array
Your initial setState() should be an object not an array.
const [state, setState] = useState(
{
name: "test",
lname: "lname",
ages: [
{
age: 30
}
]
}
);
As per the function randomAge try the following
const randomAge = () => {
let rg = ((Math.random() * 1000) / 10).toFixed();
setState({
...state,
ages: [
...state.ages,
{
age : rg
}
]
});
};
You'll see that ages within the state object has been changed to an Array. With the spread operator you're copying the previous state ages array into it. This should be smaller objects from your code and lastly you're creating another object with age : rg as the only pair value.
It might be better to keep ages as an array and simply push the rg value into it instead of creating objects. Unless you're adding multiple KEY:VALUE pairs.
Couple things of note:
.ToFixed() returns a string and not a number type
You want your setState to look like { ...state, ages: [ ...state.ages, {age: rg} ] } this is because ages is an array, and so you need to use the spread operator with the array syntax
Edit:
https://stackblitz.com/edit/react-jkgjf4?file=src/App.js
Working stackblitz, in a nutshell your object is an array so we need to treat it as such and loop over it as we are creating a new object. This way it is easier to update it than trying to use spread operators as it can quickly get very ugly.
const [state, updateState] = useState([
{
name: 'test',
lname: 'lname',
ages: [
{
age: 30,
},
],
},
]);
function update() {
//turning it into an int
let rg = Number.parseInt(((Math.random() * 1000) / 10).toFixed());
let newState = [...state];
newState.forEach((x) => x.ages.push({ age: rg }));
console.log('new State: ', newState);
updateState(newState);
}
Just change your randomAge function like this.
const randomAge = () => {
const rg = ((Math.random() * 1000) / 10).toFixed();
const newAgesArray = state[0].ages;
newAgesArray.push({ age: rg });
setState([...state, { ages: newAgesArray }]);
};
Considering only one item in the whole array, I used an index 0.
Also const newAgesArray = state[0].ages doesn't make a whole new copy of the array, it just creates a reference. It works but it's not ideal when working with state. I recommend you to do some shallow or deep copy instead - DYOR.
Let's suppose I have the following object:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
And that I want only the id and fullName.
I will do the following :
const { id, fullName } = user
Easy-peasy, right?
Now let's suppose that I want to do the destructuring based on the value of another variable called fields.
const fields = [ 'id', 'fullName' ]
Now my question is : How can I do destructuring based on an array of keys?
I shamelessly tried the following without success:
let {[{...fields}]} = user and let {[...fields]} = user. Is there any way that this could be done?
Thank you
It's not impossible to destructure with a dynamic key. To prevent the problem of creating dynamic variables (as Ginden mentioned) you need to provide aliases.
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName' ];
const object = {};
const {[fields[0]]: id, [fields[1]]: fullName} = user;
console.log(id); // 42
console.log(fullName); // { firstName: "John", lastName: "Doe" }
To get around the problem of having to define static aliases for dynamic values, you can assign to an object's dynamic properties. In this simple example, this is the same as reverting the whole destructuring, though :)
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName' ];
const object = {};
({[fields[0]]: object[fields[0]], [fields[1]]: object[fields[1]]} = user);
console.log(object.id); // 42
console.log(object.fullName); // { firstName: "John", lastName: "Doe" }
sources:
https://twitter.com/ydkjs/status/699845396084846592
https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20%26%20beyond/ch2.md#not-just-declarations
Paul Kögel's answer is great, but I wanted to give a simpler example for when you need only the value of a dynamic field but don't need to assign it to a dynamic key.
let obj = {x: 3, y: 6};
let dynamicField = 'x';
let {[dynamicField]: value} = obj;
console.log(value);
Short answer: it's impossible and it won't be possible.
Reasoning behind this: it would introduce new dynamically named variables into block scope, effectively being dynamic eval, thus disabling any performance optimization. Dynamic eval that can modify scope in fly was always regarded as extremely dangerous and was removed from ES5 strict mode.
Moreover, it would be a code smell - referencing undefined variables throws ReferenceError, so you would need more boilerplate code to safely handle such dynamic scope.
As discussed before, you can't destruct into dynamically named variables in JavaScript without using eval.
But you can get a subset of the object dynamically, using reduce function as follows:
const destruct = (obj, ...keys) =>
keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
const object = {
color: 'red',
size: 'big',
amount: 10,
};
const subset1 = destruct(object, 'color');
const subset2 = destruct(object, 'color', 'amount', 'size');
console.log(subset1);
console.log(subset2);
You can't destruct without knowing the name of the keys or using an alias for named variables
// you know the name of the keys
const { id, fullName } = user;
// use an alias for named variables
const { [fields[0]]: id, [fields[1]]: fullName } = user;
A solution is to use Array.reduce() to create an object with the dynamic keys like this:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName', 'age' ];
const obj = fields.reduce((acc, k) => ({ ...acc, ...(user.hasOwnProperty(k) && { [k]: user[k] }) }), {});
for(let k in obj) {
console.log(k, obj[k]);
}
I believe that the above answers are intellectual and valid because all are given by pro developers. :). But, I have found a small and effective solution to destructure any objects dynamically. you can destructure them in two ways. But both ways are has done the same action.
Ex:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
using "Object.key", "forEach" and "window" object.
Object.keys(user).forEach(l=>window[l]=user[l]);
Simply using Object. assign method.
Object.assign(window, user)
Output:
console.log(id, displayName, fullName)
// 42 jdoe {firstName: "John", lastName: "Doe"}
Anyway, I am a newbie in JS. So, don't take it as an offense if you found any misleading info in my answer.
Let's suppose I have the following object:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
And that I want only the id and fullName.
I will do the following :
const { id, fullName } = user
Easy-peasy, right?
Now let's suppose that I want to do the destructuring based on the value of another variable called fields.
const fields = [ 'id', 'fullName' ]
Now my question is : How can I do destructuring based on an array of keys?
I shamelessly tried the following without success:
let {[{...fields}]} = user and let {[...fields]} = user. Is there any way that this could be done?
Thank you
It's not impossible to destructure with a dynamic key. To prevent the problem of creating dynamic variables (as Ginden mentioned) you need to provide aliases.
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName' ];
const object = {};
const {[fields[0]]: id, [fields[1]]: fullName} = user;
console.log(id); // 42
console.log(fullName); // { firstName: "John", lastName: "Doe" }
To get around the problem of having to define static aliases for dynamic values, you can assign to an object's dynamic properties. In this simple example, this is the same as reverting the whole destructuring, though :)
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName' ];
const object = {};
({[fields[0]]: object[fields[0]], [fields[1]]: object[fields[1]]} = user);
console.log(object.id); // 42
console.log(object.fullName); // { firstName: "John", lastName: "Doe" }
sources:
https://twitter.com/ydkjs/status/699845396084846592
https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20%26%20beyond/ch2.md#not-just-declarations
Paul Kögel's answer is great, but I wanted to give a simpler example for when you need only the value of a dynamic field but don't need to assign it to a dynamic key.
let obj = {x: 3, y: 6};
let dynamicField = 'x';
let {[dynamicField]: value} = obj;
console.log(value);
Short answer: it's impossible and it won't be possible.
Reasoning behind this: it would introduce new dynamically named variables into block scope, effectively being dynamic eval, thus disabling any performance optimization. Dynamic eval that can modify scope in fly was always regarded as extremely dangerous and was removed from ES5 strict mode.
Moreover, it would be a code smell - referencing undefined variables throws ReferenceError, so you would need more boilerplate code to safely handle such dynamic scope.
As discussed before, you can't destruct into dynamically named variables in JavaScript without using eval.
But you can get a subset of the object dynamically, using reduce function as follows:
const destruct = (obj, ...keys) =>
keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
const object = {
color: 'red',
size: 'big',
amount: 10,
};
const subset1 = destruct(object, 'color');
const subset2 = destruct(object, 'color', 'amount', 'size');
console.log(subset1);
console.log(subset2);
You can't destruct without knowing the name of the keys or using an alias for named variables
// you know the name of the keys
const { id, fullName } = user;
// use an alias for named variables
const { [fields[0]]: id, [fields[1]]: fullName } = user;
A solution is to use Array.reduce() to create an object with the dynamic keys like this:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName', 'age' ];
const obj = fields.reduce((acc, k) => ({ ...acc, ...(user.hasOwnProperty(k) && { [k]: user[k] }) }), {});
for(let k in obj) {
console.log(k, obj[k]);
}
I believe that the above answers are intellectual and valid because all are given by pro developers. :). But, I have found a small and effective solution to destructure any objects dynamically. you can destructure them in two ways. But both ways are has done the same action.
Ex:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
using "Object.key", "forEach" and "window" object.
Object.keys(user).forEach(l=>window[l]=user[l]);
Simply using Object. assign method.
Object.assign(window, user)
Output:
console.log(id, displayName, fullName)
// 42 jdoe {firstName: "John", lastName: "Doe"}
Anyway, I am a newbie in JS. So, don't take it as an offense if you found any misleading info in my answer.