How to use string as object in redux react [duplicate] - javascript

This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed last month.
I've redux store which looks like below.
store = {
a: {
b: { value: "" }
},
c: {
d: { value: "" }
}
};
From my component, I want to pass like below to minipulate value of b.
dispatch(doSomething({ value: 'my-value', place: "a.b" });
And, in my reducers, I want to handle like below.
doSomething: (state, action) => {
const { value, place } = action.payload;
state[place].value = value; // here, i want to refere -> state.a.b.value = value;
}
I don't want to handle like this:
doSomething: (state, action) => {
const { value, place } = action.payload;
if (place === 'a.b') state.a.b.value = value;
else if (place === 'c.d') state.c.d.value = value;
}
How can I achieve it?

What you're trying to do is access/modify the object by string path. Maybe you can do something like this.
let store = {
a: {
b: { value: "" }
},
c: {}
};
console.log(store)
function setNestedValue(obj, path, value) {
let i;
path = path.split('.');
for (i=0;i<path.length-1;i++) obj = obj[path[i]];
obj[path[i]] = value
}
setNestedValue(store,"a.b", "xx")
console.log(store)

const {value, place } = payload
let cur=state;
place.split(".").forEach((key, idx)=> {
if(idx < place.length -1) {
cur[key] =value;
} else{
cur=cur[key];
}
});
sent via mobile.. couldn't format much.. you can improvise this..

Related

How to access a nested property of an object using a string?

I have the following string:
const str = "prop1.prop2.prop3"
I want to use this string to access the property prop3 of the following object:
const obj = {
prop1: {
prop2:{
prop3:{
// ---- destination point
}
}
}
}
But I'm not able to figure out how to do it?
there must be something that keeps adding the obj[currentProp] so on and so on. and.. isn't there a quicker method? I'm afraid I'm wasting my time on something that can be achieved more easily
This would be my approach:
const access = (path, object) => {
return path.split('.').reduce((o, i) => o[i], object)
}
const obj = {
prop1: {
prop2: {
prop3: {
value: 'foo'
}
}
}
}
const str = 'prop1.prop2.prop3'
console.log(access(str, obj)) // {"value": "foo"}
You can combine split with forEach as follows:
const str = "prop1.prop2.prop3"
const obj = {
prop1: {
prop2:{
prop3:{
a: "b",
c: "d"
}
}
}
}
var srch = obj;
str.split(".").forEach(item => (srch = srch[item]));
console.log(srch); // { a: "b", c: "d"}
console.log(obj);
split converts str's value into an array, which is then looped and on each iteration, srch gets one level deeper.
different ways to access a nested property of an object
using a function accessDeepProp with two arguments the object and path of the nested property!
Recursive way:
function accessDeepProp(obj, path) {
if (!path) return obj;
const properties = path.split(".");
return accessDeepProp(obj[properties.shift()], properties.join("."));
}
For-loop way:
function accessDeepProp(obj, path) {
const properties = path.split(".");
for (let i = 0; i < properties.length; i++) {
if (!obj) return null;
obj = obj[properties[i]];
}
return obj;
}
Eval way: never_use_eval!
function accessDeepProp(objName, path) {
try {
return eval(`${objName}.${path}`);
} catch (e) {
return null;
}
}
you could also use lodash get method
This is the shortest solution, and it supports arrays and ['bracket notation']. Just don't run it against malicious user input.
Update: a better(?) version without eval.
const obj = {
prop1: {
prop2: {
prop3: {
value: 'foo'
}
}
}
}
const str = 'prop1.prop2.prop3'
//console.log(eval("obj." + str))
// a code without eval
var value = (Function("return obj." + str))();
console.log(value);

how to compare if two objects have the same fields (same schema) [duplicate]

This question already has answers here:
How can I check that two objects have the same set of property names?
(9 answers)
Closed 2 years ago.
I receive a JSON object through an HTTP request.I just want to check if that object has only the fields that I want, don't need to check their value.
I did this:
function checkObject(o1, o2) {
for (const p1 in o1) {
for (const p2 in o2) {
if (o1.hasOwnProperty(p2) === false) {
return false;
}
if (o2.hasOwnProperty(p1) === false) {
return false;
}
}
}
return true;
}
This is the valid object. I want to accept only requests with this body, don't care about the values.
const validObject = {
country: '',
name: '',
zip: '',
//etc
};
after this i do an if statement like:
if(checkObject(validObject, receivedObject)){
//do stuff here
}else{
//reject the request
}
Ok this works, but it is too ugly. It does not pass the lint test and there are too many for loops. What do you think? is there an alternative to do the same thing?
Thank you!!
If you're interested there is a spec for JSON schema validation, and there are implementations for JavaScript like ajv.
Now I think this would be too much for what you need. The implementation of checkObject could be:
const valid = {
foo: '',
bar: ''
}
const okay = {
bar: '',
foo: ''
}
const notEnough = {
foo: ''
}
const tooMuch = {
foo: '',
bar: '',
foobar : ''
}
function checkObject(validObject, receivedObject) {
const validKeys = Object.keys(validObject);
const receivedKeys = Object.keys(receivedObject);
return validKeys.length === receivedKeys.length
&& validKeys.every(value => ~receivedKeys.indexOf(value));
}
console.log(checkObject(valid, okay));
console.log(checkObject(valid, notEnough));
console.log(checkObject(valid, tooMuch));
here is a lodash version
import _ from "lodash"
function checkObject(o1,o2){
return !_(o1).keys().intersection(_.keys(o2)).isEmpty();
}

Destructure object on provided parameter [duplicate]

Says my state is like this:
{
item:{
a:'a',
b:'b'
}
}
Then I'm able to pull a from item by doing:
const { a } = this.state.item
but can pull dynamically using {} of es6 ?
For example const { variable } = this.state.item, where variable can be a or b.
As 4castle pointet out, you could use Computed object property names and destructuring with an additional key/value pair variables for destructuring.
var object = { item: { a: 'a0', b: 'b0' } },
key = 'b',
value;
({ [key]: value } = object.item);
console.log(value);
const handleChange = (e) => {
const { name, value } = e.target;
const { [name]: property, ...rest } = coupon;
setNewValue({ [name]: value, ...rest });
}

Update fields in nested objects in Typescript / Javascript

In Firestore you can update fields in nested objects by a dot notation (https://firebase.google.com/docs/firestore/manage-data/add-data?authuser=0#update_fields_in_nested_objects). I wonder how to make that work in Typescript / Javascript.
For example the following object:
const user = {
id: 1
details: {
name: 'Max',
street: 'Examplestreet 38',
email: {
address: 'max#example.com',
verified: true
}
},
token: {
custom: 'safghhattgaggsa',
public: 'fsavvsadgga'
}
}
How can I update this object with the following changes:
details.email.verified = false;
token.custom = 'kka';
I already found that Lodash has a set function:
_.set(user, 'details.email.verified', false);
Disadvantage: I have to do this for every change. Is their already a method to update the object with an object (like firestore did)?
const newUser = ANYFUNCTION(user, {
'details.email.verified': false,
'token.custom' = 'kka'
});
// OUTPUT for newUser would be
{
id: 1
details: {
name: 'Max',
street: 'Examplestreet 38',
email: {
address: 'max#example.com',
verified: false
}
},
token: {
custom: 'kka',
public: 'fsavvsadgga'
}
}
Does anyone know an good solution for this? I already found more solutions if I only want to change one field (Dynamically set property of nested object), but no solution for more than one field with one method
I think you are stuck with using a function but you could write it yourself. No need for a lib:
function set(obj, path, value) {
let parts = path.split(".");
let last = parts.pop();
let lastObj = parts.reduce((acc, cur) => acc[cur], obj);
lastObj[last] = value;
}
set(user, 'details.email.verified', false);
if what you want to do is merge 2 objects then it is a bit trickier:
function forEach(target, fn) {
const keys = Object.keys(target);
let i = -1;
while (++i < keys.length) {
fn(target[keys[i]], keys[i]);
}
}
function setValues(obj, src) {
forEach(src, (value, key) => {
if (value !== null && typeof (value) === "object") {
setValues(obj[key], value);
} else {
obj[key] = value;
}
});
}
let obj1 = {foo: {bar: 1, boo: {zot: null}}};
let obj2 = {foo: {baz: 3, boo: {zot: 5}}};
setValues(obj1, obj2);
console.log(JSON.stringify(obj1));
One solution in combination with lodash _.set method could be:
function setObject(obj, paths) {
for (const p of Object.keys(paths)) {
obj = _.set(obj, p, paths[p]);
}
return obj;
}

Destructuring of es6 but passing dynamic variable

Says my state is like this:
{
item:{
a:'a',
b:'b'
}
}
Then I'm able to pull a from item by doing:
const { a } = this.state.item
but can pull dynamically using {} of es6 ?
For example const { variable } = this.state.item, where variable can be a or b.
As 4castle pointet out, you could use Computed object property names and destructuring with an additional key/value pair variables for destructuring.
var object = { item: { a: 'a0', b: 'b0' } },
key = 'b',
value;
({ [key]: value } = object.item);
console.log(value);
const handleChange = (e) => {
const { name, value } = e.target;
const { [name]: property, ...rest } = coupon;
setNewValue({ [name]: value, ...rest });
}

Categories

Resources