Pass value and use as object properties dynamically in Javascript - javascript

Is there a way I can pass a parameter value into a function and use it as properties of an object?
const state = {
name: 'xyz',
properties: 'abc'
}
...
const handleStuff = (properties:string) => {
const a = [...state.properties]
if (action.status == true)
{
//some code
return {
...state,
properties: a
}
} else {
//some code
return {
...state,
properties: a
}
}
}

It is still not clear what result you are trying to reach, but generally you can access your properties by [] operator.
if you want just to store state.name value into variable you should do following
const a = state[properties] // a will be 'xyz'
code below will be evaluated as spread operation performed on the string 'xyz'
const a = [...state[properties]] // a will be equal to ['x', 'y', 'z']
in your return statement, where you want to combine object and if you want to assign value to property with name properties (which is 'name' for example) you can
return {
...state,
[properties]: a // name value from the state will be overridden with value of variable a
};

Related

Change Nested State Value With A Single Function? (javascript/React)

I have some react user privilege state data I need to manage. I would like the ability to change the object privileges based on their property through a dynamic function. I'm not sure how to target the specific nested privilege property to change the value. Is this possible?
Question: How can I change the value of a nested privilege property to the functions type and value parameter?
Heres an Example:
const [userPrivilages, setUserPrivilages] = useState([{
_id: "123"
privilages: {
edit: true, //before!
share: true,
del: false
}
},
{
...more users
}
])
//my attempt
const changePrivilage = (type, value) => {
const newPrivilages = userPrivilages.map(user => {
return {
...user,
privilages: {
...privilages,
//change the privilage of "type" from the functions parameter to the value parameter
}
}) setUserPrivilages(newPrivilages)
}
changePrivilage("edit", false)
Desired output:
const [userPrivilages, setUserPrivilages] = useState([{
_id: "123"
privilages: {
edit: false, //After!
share: true,
del: false
}
},
{
...more users
}
])
Thanks!
You can use [] to refer to variable as a key like below:
const changePrivilage = (type, value) => {
const newPrivilages = userPrivilages.map(user => {
return {
...user,
privilages: {
...user.privilages,
[type]: value // here it is !!!
}
}) setUserPrivilages(newPrivilages)
}
Try this :
(see comments for understanding code)
const changePrivilage = (type,value) => {
const newUserPrivilages = userPrivilages.map(user => {
let newPrivilages = user.privilages; // get old privilages of user
newPrivilages[type] = value; // update type with new value
return {
...user,
privilages: newPrivilages, // set privilages as newPrivilages
};
});
setUserPrivilages(newUserPrivilages);
};
Note : this will change properties for all users. If you want to update only for specific user, pass _id as well to changePrivilage and execute newPrivilages[type] = value; // update type with new value inside if condition comparing user _id.

How to destructure a nested object only when it has a value?

let notStudent, name, isRegistered
if (studentDetail && studentDetail.fields) {
({ notStudent, name, isRegistered } = studentDetail.fields)
}
Is there a way to write this logic without an if statement or in a succinct way?
You can destructure in this way. The tricky thing is when there is no fields property on studentDetail then javascript can throw an error, to tackle that case, you can set default empty object using || operator.
let studentDetail = {
fields: {
notStudent: '1',
name: '2',
isRegistered: true
}
}
let {
notStudent,
name,
isRegistered
} = (studentDetail && studentDetail.fields) || {};
console.log(notStudent);
console.log(name);
console.log(isRegistered);
You can destructure an empty default object in case your studentDetail.fields doesn't exist:
const { notStudent, name, isRegistered } = studentDetail?.fields ?? {};

Extract object name as String from an object in JavaScript

I have an object where its values are other objects
I would like to extract the name of the object by its parent key as a string, e.g., (input, expected output) = ('home', 'someObj), ('anotherOne', 'anotherObj').
So far I tried the following, but it returns [object Object].
I also tried JSON.stringify(data[key].key1) but does not return what I want. Is there a way to achieve this?
const someObj = {
something: 'la'
}
const anotherObj = {
something: 'be'
}
const data = {
'home': {
key1: someObj
},
'anotherOne': {
key1: anotherObj
}
}
console.log(data)
const key = 'home'
const output = `${data[key].key1}`
console.log(output) // expected output: 'someObj'
Javascript objects don't have names. You'd have to take care yourself and probably add a 'name' field where you set an identifier on an object instance.
You hope, that the 'name' of the object could be like the name of the variable that you assign it to. But that's not true. There is zero connection between the object instance and the variable name.
The problem is that you are assigning the variable value and not its name. Because JS compiler will not keep track of the variable name that it used to assign values to
so,
const someObj = {
something: 'la'
}
const anotherObj = {
something: 'be'
}
const data = {
'home': {
key1: someObj
},
'anotherOne': {
key1: anotherObj
}
}
JS compiles it as
const data = {
'home': {
key1: {
something: 'la' // Doesn't keep track of the original variable name used to assign value.
}
},
'anotherOne': {
key1: {
something: 'be'
}
}
}
And when you fetch data[key].key1. it returns {something: 'la'} which is an object and when you use string template literal `${data[key].key1}`. It typecast it into string as
`${{}}` // Outputs "[object Object]"
So, you need to store the variable names as string
const someObj = {
something: 'la'
}
const anotherObj = {
something: 'be'
}
const data = {
'home': {
key1: 'someObj'
},
'anotherOne': {
key1: 'anotherObj'
}
}
console.log(data)
const key = 'home'
const output = `${data[key].key1}`
console.log(output) // output: 'someObj'
This code will work
You need to set the variable name as string.

ReactJS: How to access and update nested state object with dynamic key?

Suppose I have a component with state defined as follows:
this.state = {
apple:{
a:1,
b:2,
},
mango:{
banana : {
a:1,
b:2,
}
}
}
If I wanted to update the value of a nested object in my state, I could do so with hard coded keys as shown below:
cost temp = { ...this.state['mango'] }
temp['banana']['a'] = 2;
this.setState({mango:temp});
How would I update a nested value in my state object dynamically key? For example, if I had a JSON path in either dot or array notation, how could I update my component state?
One way to achieve this would be to acquire the nested object that is the parent of the field that your path is targeting via Array#reduce:
const nestedObject = path
.slice(0, -1)
.reduce((object, part) => (object === undefined ? undefined : object[part]), { ...state })
And then update the last key/value of nestedObject by via the last key of your path:
/* Get last part of path, and update nestedObject's value for this key, to 2 */
const [pathTail] = path.slice(-1);
nestedObject[pathTail] = 2;
The following snippet shows these two ideas together:
/* Path of nested field to update, in array notation */
const path = ['mango', 'banana', 'a'];
/* Components state */
const state = {
apple: {
a: 1,
b: 2,
},
mango: {
banana: {
a: 1,
b: 2,
}
}
};
const stateClone = { ...state };
/* Aquire the parent object (ie banana) of the target field (ie a) */
const nestedObject = path
.slice(0, -1)
.reduce((object, part) => (object === undefined ? undefined : object[part]), stateClone)
if (nestedObject !== undefined) {
/* Obtain last key in path */
const [pathTail] = path.slice(-1);
/* Update value of last key on target object to new value */
nestedObject[pathTail] = 2;
}
/* Display updated state */
console.log('Updated state:', stateClone)
/* Call this.setState: */
// this.setState(stateClone);
Update
Here is some extra detail outlining how the reduce() part of the answer works:
path
/* slice obtains ['mango', 'banana'], seeing -1 clips last item */
.slice(0, -1)
/* reduce iterates through each part of array ['mango', 'banana']
where at each iteration we fetch the corresponding nested object
of the { ...state } object that's passed in */
.reduce((object, part) => {
/* At iteration 1:
object has two keys, 'apple' and 'mango'
part is 'mango'
object is defined, so return object['mango'] for first iteration
At iteration 2:
object passed from last iteration has one key, 'banana'
part is 'banana'
object is defined, so return object['banana'] for second iteration
Reduce complete:
we return object['banana'], which is the same as state['mango']['banana']
*/
if(object === undefined) { return undefined; }
return object[part]
}, stateClone)
Having:
const [formState, setFormState] = useState(
{
id:1,
name:'Name',
innerObjectName: {
propA: 'Something',
propB: 'Another thing',
}
});
Maybe you're looking for something like this:
const handleComplexInputChange = (evt, object) => {
setFormState({
...formState,
[object] : {
...formState[object],
[evt.target.name]: evt.target.value,
}
})
}
And from your component you should call it like this:
onChange={(e) => {
handleComplexInputChange(e, "innerObjectName");
}}

Nest/Group an array of objects by attribute, ignoring null/undefined and using additional property as label

I'm trying to nest-group an array of objects.
The function provided by this gist almost works as intended and uses lodash as basis:
https://gist.github.com/joyrexus/9837596
const _ = require('lodash');
function nest(seq, keys) {
if (!keys.length) return seq;
let [first, ...rest] = keys;
return _.mapValues(_.groupBy(seq, first), value => nest(value, rest));
}
This recursively,
However, there are two problems I face.
if a parameter is set to null or undefined, it is used as a group, instead the
an optional object attribute should be used as the final object key, so there are only objects, no arrays. This attribute always has to be unique in order to work correctly.
Is it possible to combine or extend the existing nest function to solve the above points?
The pros of this method is, that instead of the keys, I can also use an array of functions (p => p.parameterGroup1) to return the parameter. So instead of a last optional parameter, I could also use p => p.parameterGroup1 ? p.parameterGroup1 : p.label
I prepared a little test, to give you a better idea of what is expected:
test('nest array of objects by groups as keys, stopping at null and using a final label param', t => {
let properties = [
{
parameterGroup1: 'first',
parameterGroup2: 'second',
parameterGroup3: 'third',
label: 'A'
},
{
parameterGroup1: 'first',
parameterGroup2: 'second',
parameterGroup3: null,
label: 'B'
},
{
parameterGroup1: 'a',
parameterGroup2: 'b',
parameterGroup3: undefined,
label: 'C'
},
]
let expected = {
first: {
second: {
third: {
A: {
parameterGroup1: 'first',
parameterGroup2: 'second',
parameterGroup3: 'third',
label: 'A'
}
},
B: {
parameterGroup1: 'first',
parameterGroup2: 'second',
parameterGroup3: null,
label: 'B'
}
}
},
a: {
b: {
C: {
parameterGroup1: 'a',
parameterGroup2: 'b',
parameterGroup3: undefined,
label: 'C'
}
}
}
}
let grouped = nest(properties, ['parameterGroup1', 'parameterGroup2', 'parameterGroup3'], 'label')
t.deepEqual(grouped, expected)
})
Thank you in advance!
Here is a way to do it in vanilla js. We construct the result object by reduceing the array seq: For each object obj in the array seq, we walk the result object level by level using the values from obj of the keys from keys. If the value is null or undefined, we skip (won't go down another level). If the value exist we go down a level, creating a level (object) if it doen't already exist. We do that repeatedly using a reduce on the keys array untill we find the leaf object (last level), to which we assign our current object under the key obtained evaluating obj[last]:
function nest(seq, keys, last) {
return seq.reduce((result, obj) => {
// First we find the (last level) object to which we will assign our current object to, as a child
let lastLevel = keys.reduce((res, key) => { // for each key in keys
let value = obj[key]; // get the value from our current object obj for that key key
if(value == null) return res; // if the value is null or undefined, skip
if(res[value]) return res[value]; // if the level for value exists return it
return res[value] = {}; // if it doesn't, create a new level, assing it to result and return it
}, result);
// then we assign it using the value of the key last
lastLevel[obj[last]] = obj; // we found the last possible level, assign obj to it under the key obj[last]
return result;
}, {});
}
Example:
function nest(seq, keys, last) {
return seq.reduce((result, obj) => {
let lastLevel = keys.reduce((res, key) => {
let value = obj[key];
if(!value) return res;
if(res[value]) return res[value];
return res[value] = {};
}, result);
lastLevel[obj[last]] = obj;
return result;
}, {});
}
let properties = [{parameterGroup1: 'first',parameterGroup2: 'second',parameterGroup3: 'third',label: 'A'},{parameterGroup1: 'first',parameterGroup2: 'second',parameterGroup3: null,label: 'B'},{parameterGroup1: 'a',parameterGroup2: 'b',parameterGroup3: undefined,label: 'C'}];
let grouped = nest(properties, ['parameterGroup1', 'parameterGroup2', 'parameterGroup3'], 'label');
console.log(grouped);

Categories

Resources