This question already has answers here:
How to set object property (of object property of..) given its string name in JavaScript?
(16 answers)
Closed 1 year ago.
I have this object {country:{town:{company:{boss:{}}}}} and this string country.town.cityhouse.major, I need to updatr the object from the string, but keeping the previous properties and data.
{
country: {
town: {
company: {
boss: {}
},
cityhouse: {
major: {}
}
}
}
}
This is what I have so far:
function updateObj(object, path) {
const newPath = path.split('.');
let temp = {...object};
for (let i = 0; i < newPath.length; i++) {
let mid = temp[newPath[i]];
if (mid) {
temp = mid;
} else {
temp[newPath[i]] = {};
}
}
return temp;
}
const obj = { country: { town: { company: { boss: {} }}}};
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
but it responds:
{
company: {
boss: {}
},
cityhouse: {},
major: {}
}
Any hint on this?
You can clean this up a little using logical nullish assignment(??=) and a for...of loop.
function updateObj(object, path) {
let result = { ...object };
let temp = result;
for (const k of path.split('.')) {
temp = temp[k] ??= {};
}
return result;
}
const obj = { country: { town: { company: { boss: {} } } } };
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Recursive option
function updateObj(object, path) {
const result = { ...object };
const [prop, ...pathArr] = path.split('.');
result[prop] = pathArr.length
? updateObj((result[prop] ??= {}), pathArr.join('.'))
: (result[prop] ??= {});
return result;
}
const obj = { country: { town: { company: { boss: {} } } } };
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This code would normally be written recursively, so you need to have a variable to hold the current scope (the path you walked on through the object) to do the job, and so i created the variable called scope to do that job.
function updateObj(object, path) {
const newPath = path.split('.');
let temp = {...object};
let scope = temp;
for (let i = 0; i < newPath.length; i++) {
let cur = scope[newPath[i]];
if (!cur) {
scope = scope[newPath[i]] = {};
} else {
scope = cur;
}
}
return temp;
}
const obj = { country: { town: { company: { boss: {} }}}};
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
This is a recursive implementation, but since it requires copying the object and passing it to the next recursive call it's not an efficient one, If i find a better more efficient implementation, I'll update this.
function updateObjRec(object, path, depth = 0) {
// base case
// if depth is equal to path length then it's over
if (depth === path.length) {
return {};
}
const cur = path[depth];
const scope = object[cur];
// here we have 2 cases
// current field already exists
update = {};
if (scope) {
update = updateObjRec({ ...scope}, path, depth + 1);
} else {
update = updateObjRec({}, path, depth + 1);
}
// merge with given object
object[cur] = update;
return object
}
const obj = { country: { town: { company: { boss: {} }}}};
const r = updateObjRec(obj, 'country.town.cityhouse.major'.split('.'));
console.log(r);
Update
The recursive code can be rewritten this way to be more efficient
function updateObj(obj, path) {
const temp = { ...obj };
const p = path.split('.');
const updateRec = (scope, depth = 0) => {
// base case
if (depth === p.length) return;
const cur = p[depth];
if (!scope[cur]) {
scope[cur] = {};
}
updateRec(scope[cur], depth + 1);
}
updateRec(temp);
return temp;
}
const obj = { country: { town: { company: { boss: {} }}}};
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
This isn't ideal, but it's a start.
Basically, you're returning temp, which is the lowest layer. you want to return the root.
Also when you can't find the next layer, you were creating it, but you weren't updating temp.
function updateObj(object, path) {
const newPath = path.split('.');
let temp = {...object};
const result = temp; // need to return this
for (let i = 0; i < newPath.length; i++) {
let mid = temp[newPath[i]];
if (mid) {
temp = mid;
} else {
temp[newPath[i]] = {};
temp = temp[newPath[i]] // need this
}
}
return result;
}
const obj = { country: { town: { company: { boss: {} }}}};
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
I think this is what you want to achieve
const obj = {
country: {
town: {
company: { boss: {} },
},
},
};
obj.country.town.cityhouse = {
major: {},
};
console.log(obj);
Related
I want to check the value of every key in each object and write its length.
I tried doing this:
const a = [
{
name:"Bill",
age:'',
old:''
}
]
const myF = (arr) => {
return arr.map((i,k) => {
console.log(Object.keys(i))
return{ [Object.keys(i)]: ''}
})
}
console.log(myF(a))
I expect to get:
{
name:4,
age:0,
old:0
}
You can map it by taking entries. Let me know if this is what something you need:
var a = [ { name:"Bill", age:'', old:''}];
var result = a.map(obj=>Object.fromEntries(Object.entries(obj).map(([k,v])=>[k, v ? v : v.length])));
var result2 = a.map(obj=>Object.fromEntries(Object.entries(obj).map(([k,v])=>[k, v.length])));
console.log(result);
console.log(result2)
const a = [
{
name:"Bill",
age:'',
old:''
}
]
var b = a.map((x) =>{
if(x.age == '') {
x.age = 0;
}
if(x.old == '') {
x.old = 0;
}
return x;
})
console.log(b)
I have an JS array of objects as below:
[
{
key1:"value1",
key2:"value2",
key3:"value3"
},
{
key1:"value1",
key2_updated:"value2_updated",
key3_updated:"value3_updated"
}
]
Post transformation , it must be of type object as below:
{
value1: {
key2:"value2",
key3:"value3",
key2_updated:"value2_updated",
key3_updated:"value3_updated"
}
}
Notice In array , first key-value of each object is same and hence i wanted to merge them and create new object with value1:mergecontent and if it is not same ,, then create new k-v in same object.
Group the array of object by key1 first, then assign the attributes
var data = [
{
key1:"value1",
key2:"value2",
key3:"value3"
},
{
key1:"value1",
key2_updated:"value2_updated",
key3_updated:"value3_updated"
},
{
key1:"value2",
key2_updated:"value2_updated",
key3_updated:"value3_updated"
}
];
var result = data.reduce((x, v) => {
x[v.key1] = x[v.key1] || {}
const { key1, ...res } = v
x[v.key1] = Object.assign(x[v.key1], res)
return x
}, {})
var newResult = data.reduce((x, v) => {
x[v.key1] = x[v.key1] || {}
const { key1, ...res } = v
var newObj = {}
var currentKey
for (let [key, value] of Object.entries(res)) {
if (currentKey) {
newObj[currentKey] = value;
currentKey = null;
} else {
newObj[value] = "";
currentKey = value
}
}
x[v.key1] = Object.assign(x[v.key1], newObj)
return x
}, {})
function transform(d, k){
return d.reduce((x, v) => {
x[v[k]] = x[v[k]] || {}
const { key1, ...res } = v
var newObj = {}
var currentKey
for (let [key, value] of Object.entries(res)) {
if (currentKey) {
newObj[currentKey] = value;
currentKey = null;
} else {
newObj[value] = "";
currentKey = value
}
}
x[v[k]] = Object.assign(x[v[k]], newObj)
return x
}, {})
}
console.log(transform(data, "key1"))
As a part of a challenge I need to implement the .findKey() method myself. Below is the solution proposition, however, I get an error "predicate is not a function".
const _ = {
findKey(object, predicate) {
for (let key in object) {
let value = object[key];
let predicateReturnValue = predicate(value);
if (predicateReturnValue) {
return key;
};
};
undefined
return undefined;
}
};
Can anyone help?
function findKey(object, predicate) {
for (let key in object) {
let value = object[key];
let predicateReturnValue = predicate(value);
if (predicateReturnValue) { // just take the value
return key; // return key
}
}
}
const isEqual = a => b => a === b
const object = { a: 'Apple', b: 'Beer', c: 'Cake' }
alert(findKey(object, isEqual('Apple')));
alert(findKey(object, isEqual('Cakes')));
I have the below recursion function:
const getDiff = (object, base) => {
const changes = (object, base, path = "") => {
return _.transform(object, function(result, value, key) {
if (!_.isEqual(value, base[key])) {
if (_.isObject(value) && _.isObject(base[key])) {
if (!path) {
path += `${key}`;
} else {
path += `.${key}`;
}
result[key] = changes(value, base[key], path);
} else {
if (!path) {
path = key;
}
result[key] = { value, path };
}
}
});
};
return changes(object, base);
};
I'm trying to make sure that it returns not just the properties that are different, but also the direct path of that property.
For instance, if I have,
const objA = {
filter: {
tag: 1
},
name: 'a'
}
const objB = { name: 'b' };
Then getting the diff should yield:
{
filter: {
value: { tag: 1 },
path: 'filter.tag'
},
name: {
value: 'a',
path: 'name'
}
}
But right now it returns path: 'filter' always. What am I doing wrong?
Below link for quick access to lodash for the console:
fetch('https://cdn.jsdelivr.net/npm/lodash#4.17.4/lodash.min.js')
.then(response => response.text())
.then(text => eval(text))
Your current path is scoped outside of _.transform thus it will apply to all branches.
Scope it within the result of _.transform and everything is ok.
E.g.:
const getDiff = (object, base) => {
//changed
const changes = (object, base, _path = "") => {
return _.transform(object, function(result, value, key) {
// changed
let path = _path;
if (!_.isEqual(value, base[key])) {
if (_.isObject(value) && _.isObject(base[key])) {
if (!path) {
path += `${key}`;
} else {
path += `.${key}`;
}
result[key] = changes(value, base[key], path);
} else {
if (!path) {
path = key;
} else { // changed
path += `.${key}`;
}
result[key] = { value, path };
}
}
});
};
return changes(object, base);
};
I am trying to learn how to cope with the objects and arrays and I saw many ways of iterating objects but recursing doesn't work for me and I don't understand why. What am I doing wrong?
I need to loop through an object and just slightly change something in an array. In my case, it's uppercasing the keys
Here is what I've got for now
const development = {
port: 8080,
db: {
username: "jkdfs",
password: "dsfsdg",
name: "kslfjskd",
test: { test: 12, another: 'value' }
},
token_secret: "jfkjdsj",
hash_rounds: "kjsfkljdfkl"
};
function throughObject(obj) {
let collection = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key];
if (typeof obj[key] !== 'object') {
collection[key.toUpperCase()] = value;
} else {
collection[key.toUpperCase()] = nestedObject(obj[key]);
}
}
function nestedObject(nested) {
const sub = {};
for (const k in nested) {
let v = nested[k];
if (typeof nested[k] !== 'object') {
sub[k.toUpperCase()] = v;
} else {
nestedObject(v);
}
}
return sub;
}
}
return collection;
}
const r = throughObject(development);
console.log(r);
When you're recursively calling the function on an object value, you still need to assign it to the sub object: sub[k.toUpperCase()] = nestedObject(v). Also, you don't need 2 different functions.
const development = {
port: 8080,
db: {
username: "jkdfs",
password: "dsfsdg",
name: "kslfjskd",
test: { test: 12, another: 'value' }
},
token_secret: "jfkjdsj",
hash_rounds: "kjsfkljdfkl"
};
function nestedObject(nested) {
const sub = {};
for (const k in nested) {
const v = nested[k];
if (typeof nested[k] !== 'object')
sub[k.toUpperCase()] = v;
else
sub[k.toUpperCase()] = nestedObject(v); // <- HERE
}
return sub;
}
console.log(nestedObject(development))
Here's a shorter version using Object.fromEntries()
const development={port:8080,db:{username:"jkdfs",password:"dsfsdg",name:"kslfjskd",test:{test:12,another:"value"}},token_secret:"jfkjdsj",hash_rounds:"kjsfkljdfkl"};
const convert = o =>
Object.fromEntries(
Object.entries(o).map(([k, v]) =>
[k.toUpperCase(), Object(v) === v ? convert(v) : v]
)
)
console.log(convert(development))