Sometimes you want to describe how to access specific value in a nested object with an array of string consisiting a path to that specific object. So for example, if you have some js object like this :
const sampleObject = {
a:{
deep: {
javascript: {
object: 'I want this value'
}
}
}
}
You may describe I want this value
with an array of "json object path" like this :
const pathToTheValue = ["a", "deep", "javascript", "object"]
It is very clear, and you can write an read
function which is used to retrieve the value in the path ( I want this value
) like this :
const read = (obj, path) => {
let o = obj;
for(p of path) {
o = o[p];
}
return o;
}
const pathToTheValue = ["a", "deep", "javascript", "object"]
read(sampleObject, pathToValue);
// you will get 'I want this value'
Now, comes a much more harder part. How do we write the value of the path?
If the object we want to write is purely a nested object, you may be able to use several approaches in these stack overflow answers like this :
const write = (object, path, value) => {
return path.reduceRight((obj, next, idx, fullPath) => {
if(idx+1 === fullPath.length) {
return {[next]: value}
} else {
return {[next]: obj}
}
}, object);
}
const pathToTheValue = ["a", "deep", "javascript", "object"]
write({}, pathToValue, 'I want to write this value');
However Some library like soya-form keeps their internal data in non-pure nested object
. It may have an array inside the nested object. This is an example on how soya-form keep their object :
const exampleNonPureNestedObject = {
anObject:{
whichHas:{
someArray: [
'singularValue1',
'singularValue2',
]
}
andAlso:{
someArrayOfObject: [
{likeThis: 'value1 I want to Write'},
{likeThis: 'value2'}
]
}
}
}
Now you can't use those stack overflow answers anymore.
I write a recursive function which is adapted from this stackoverflow answer such we can write a dynamic nonPure Nested Object :
const write = (obj, keys, v) => {
if (keys.length === 0) {
return v;
}
if (keys.length === 1) {
obj[keys[0]] = v;
} else {
const [key, ...remainingKeys] = keys;
const nextKey = remainingKeys[0];
const nextRemainingKeys = remainingKeys.slice(1);
if (typeof nextKey === "number") {
// create array
if (!obj[key]) {
obj[key] = [];
}
// Fill empty index with empty object
if (obj[key].length < nextKey + 1) {
delta = nextKey + 1 - obj[key].length;
for (let i = 0; i < delta; i++) {
obj[key].push({});
}
}
// recursively write the object
obj[key][nextKey] = write(obj[key][nextKey], nextRemainingKeys, v);
} else {
// recursively write the object
obj[key] = write(
typeof obj[key] === "undefined" ? {} : obj[key],
remainingKeys,
v
);
}
}
return obj;
};
Now if you want to write value 1 I want to write
into exampleNonPureNestedObject
You can simply use it like this:
const pathToValue = ['anObject','andAlso','someArrayOfObject',0,'likeThis'] // note the integer index
write({}, pathToValue, 'value 1 I want to write')
The write
function can be extended into composableFunction
.
const composableWrite = (path, value) => (object) => write(object, path, value);
Now you can chain the write function using compose
function as seen in redux
.
// before :
const objectToModify = {
existingKey: 'value'
}
const output = compose(
composableWrite(['anotherKey',0],'value 1'),
composableWrite(['anotherKey',1],'value 2'),
composableWrite(['arrayOfObject',0,'a'],'randomValue A'),
composableWrite(['arrayOfObject',1,'b'],'another value')
)(objectToModify)
console.log(output)
/**
output :
{
existingKey: 'value',
anotherKey:[
'value 1',
'value 2'
],
arrayOfObject: [
{a: 'randomValue A'},
{b: 'another value'}
]
}
**/
TODO : Translate object to path.