Javascript: updating an object's property - javascript

I'm creating a function and stuck on this prompt:
updateObject() : Should take an object, a key and a value. Should update the property key on object with new value. If does not exist on create it.
This is my code:
function updateObject(object, key, value) {
if (object.key) {
return object.key = value;
} else if (object.key === false) {
return object.key;
}
}
This is the test it's trying to pass:
QUnit.test("updateObject() : Should take an object, a key and a value. Should update the property <key> on <object> with new <value>. If <key> does not exist on <object> create it.", function(assert){
var data = {a: "one", b: "two", "hokey": false};
assert.deepEqual(updateObject(data, "b", "three"), {a:"one", b:"three", hokey: false});
var data = {a: "one", b: "two", "hokey": false};
assert.deepEqual(updateObject(data, "ponies", "yes"), {a:"one", b:"two", hokey: false, ponies: "yes"});
var data = {a: "one", b: "two", "hokey": false};
assert.deepEqual(updateObject(data, "a", Infinity), {a:Infinity, b:"two", hokey: false});
});
What am I doing wrong?

Use square bracket if there is a need to access object key using a variable.Also hasOwnProperty can be used to check if object has a key
function updateObject(object, key, value) {
if (object.hasOwnProperty(key)) {
return object[key] // will return value of the key in that object
} else {
return object[key]=value; // will create a new key and will assign value
}
}

You need to return the updated object from the function for the tests to pass.
function updateObject(object, key, value) {
object[key] = value;
return object;
}

Related

How to use variable value directly in a dynamically created JavaScript getter?

I have the following code for adding getters to an object on the fly:
const obj = {};
const uni = { name1: ..., name2: ..., ...};
for(const nm in uni) {
const u = obj[nm] = {};
Object.defineProperty(u, 'value', {
configurable: true,
enumerable: true,
get: () => { return uni[nm] },
});
}
I want getters to return different values, so I want uni[nm] to be understood literary, i.e. the first getter would return uni.name1, the second uni.name2 etc.
How do I do that?
EDIT:
I want to dynamically create an object like this:
{
name1: { get value() { return uni.name1 }},
name2: { get value() { return uni.name2 }},
...
}
Don't use for..in. And you pretty much never need Object.defineProperty. That's all stone age JavaScript.
Instead use -
for..of to iterate over Object.keys.
get keyword to define a getter
const uni = { name1: "foo", name2: "bar" }
let obj = {}
for(const key of Object.keys(uni)) {
obj[key] = {
get value() {
return uni[key]
}
};
}
console.log(obj.name1.value) // "foo"
console.log(obj.name2.value) // "bar"
uni.name1 = "zzz"
console.log(obj.name1.value) // "zzz"
This can be expressed using array.reduce and -
object spread to extend the definition of an object
computed properties to define a dynamic key -
const uni = { name1: "foo", name2: "bar" }
const obj = Object.keys(uni).reduce(
(o, key) => {
return {...o, [key]: { get value() { return uni[key] }}} // extend
},
{} // initial object
)
console.log(obj.name1.value) // "foo"
console.log(obj.name2.value) // "bar"
uni.name1 = "zzz"
console.log(obj.name1.value) // "zzz"
You can achieve this by using the square bracket notation ([]) to dynamically access properties in the uni object, like this:

How can I convert a string containing object paths and values to an object?

I want to convert strings of the form 'a|b|c', val1 and 'a|d', val2 to a nested object of the form {a: {b : {c : 'val1'}, d: 'val2'}}. I tried the following -
const path2Obj = (path, value) => {
const obj = {};
const pathComps = path.split('|').reverse();
pathComps.forEach((comp, ind) => {
if (ind) {
obj[comp] = obj;
} else {
obj[comp] = value;
}
});
return obj;
};
console.log(path2Obj('a|b|c', 'val1'));
but it logs <ref *1> { c: 'val1', b: [Circular *1], a: [Circular *1] }. Any ideas?
Background to my question
I am storing nested objects whose structure is not known before runtime to a redis database. Redis does not appear to support nested objects natively so I first convert the them to path / value pairs of strings and save them as hashes. This works but I need a way to convert them back to objects
You can basically use reduce, iterate over the path array which you created with split and then either return the value from the object if it exists, otherwise add value (new nested object or value param).
const obj = {}
const path2Obj = (path, value, obj) => {
path.split('|').reduce((r, e, i, arr) => {
return r[e] || (r[e] = i === arr.length - 1 ? value : {})
}, obj)
};
path2Obj('a|b|c', 'val1', obj)
path2Obj('a|d', 'val2', obj)
console.log(obj)

How to assign two objects with both having key variables

function updateObjectWithKeyAndValue (object, key, value) {
object = { [key]: value };
let new_object = {[key] : value};
return Object.assign(object, new_object);
}
Error:
Objects updateObjectWithKeyAndValue(object, key, value) returns an object with the original key value pairs and the new key value pair:
Error: Expected { prop2: 2 } to match { prop: 1, prop2: 2 }
If you want your function to update provided object with specific key and value, you just need this:
function updateObjectWithKeyAndValue (object, key, value) {
if (typeof object === 'object' && object !== null) {
object[key] = value;
}
}

Copying an object to another by key(s) compare

I am trying to copy a object to another by comparing it's key. according to me the key can be nested any of the object node. still I am require to copy them in another object by just comparing their key.
they object key in any level in the object tree.
Is this possible?
here is my try:
var extend = function (original, context, key) {
for (key in context)
if (context.hasOwnProperty(key))
if (Object.prototype.toString.call(context[key]) === '[object Object]')
original[key] = extend(original[key] || {}, context[key]);
else
original[key] = context[key];
return original;
};
var firstObject = {
key1 : 'ar',
key2 : 'value2',
address:{
"street" : "D15 Road"
"sub":{"name":"Stockoverflow"}
}
};
var secondObject = {
name:"",
street:"",
key3 : 'value3',
key4 : 'value4'
};
var x = extend(secondObject, firstObject )
console.log( 'extend', x );
In my above try, still the street:"", name:"" not updated.
This should do the trick:
const source = {
key1: 'a',
common1: 'value1',
a: {
b: {
c: {
common2: 'value2'
}
}
}
};
const dest = {
key2: 'b',
common1: null,
common2: null
};
function extend(dest, source) {
Object.entries(source).forEach(([key, value]) => {
if (typeof value === 'object') {
extend(dest, value);
} else if (dest.hasOwnProperty(key)) {
dest[key] = value;
}
});
}
extend(dest, source);
console.log(dest);
Basically, looping through them you only have two options: it's an object or it's key matches.
If it is an object, we just recursively call extend. If it's key matches, then we set the value.
If you want to be able to do this with objects as values (i.e., common1 could be an object instead of a primitive), just switch the two in the if statement.

How to convert array of object to nested object and vise versa in js

Array of objects where the hierarchy objects is stored in hierarchy
property. nesting of objects are done based on this hierarchy
[
{
"hierarchy" : ["obj1"],
"prop1":"value"
},
{
"hierarchy" : ["obj1","obj2"],
"prop2":"value",
"prop3":"value"
},
{
"hierarchy" : ["obj1","obj3"],
"prop4":"value",
"prop5":"value"
},
{
"hierarchy" : ["obj1","obj3", "obj4"],
"prop6":"value",
"prop7":"value",
"arr" :["val1", "val2"]
}
]
Expected nested object, hierarchy key removed here
{
"obj1":{
"prop1":"value",
"obj2" : {
"prop2":"value",
"prop3":"value"
},
"obj3":{
"prop4":"value",
"prop5":"value",
"obj4" : {
"prop6":"value",
"prop7":"value",
"arr" :["val1", "val2"]
}
}
}
}
Code I tried but at line 8 unable to get the hierarchy
var input = "nested array as above";
var output = {};
var globalTemp = output;
for(var i = 0 ; i<input.length ; i++){
var tempObj = input[i];
for(var key in tempObj){
if(key == "hierarchy"){
globalTemp = globlalTemp[tempObj[key]] = {};
}
}
}
console.log(globalTemp);
You can use forEach and reduce methods and inside create shallow copy of current object and delete hierarchy property.
const data = [{"hierarchy":["obj1"],"prop1":"value"},{"hierarchy":["obj1","obj2"],"prop2":"value","prop3":"value"},{"hierarchy":["obj1","obj3"],"prop4":"value","prop5":"value"},{"hierarchy":["obj1","obj3","obj4"],"prop6":"value","prop7":"value","arr":["val1","val2"]}]
const result = {}
data.forEach(function(o) {
o.hierarchy.reduce(function(r, e) {
const clone = Object.assign({}, o);
delete clone.hierarchy
return r[e] = (r[e] || clone)
}, result)
})
console.log(result)
With a newer version of javascript, you could use restparameters for the wanted value key/value pairs and build a nested structure by iterating the given hierarchy property by saving the last property for assigning the rest properties.
The reclaimed part getFlat uses an array as stack without recursive calls to prevent a depth first search which tries to get the most depth nodes first.
At start, the stack is an array with an array of the actual object and another object with an empty hierarchy property with an empty array, because actually no key of the object is known.
Then a while loop checks if the stack has some items and if so, it takes the first item of the stack and takes a destructuring assignment for getting an object o for getting back all key/value pairs and another object temp with a single property hierarchy with an array of the path to the object o.
The push flag is set to false, because only found properties should be pushed later to the result set.
Now all properties of the object are checked and if
the value is truthy (to prevent null values),
the type is an object (null is an object) and
the property is not an array
then a new object is found to inspect. This object is pushed to the stack with the actual path to it.
If not, then a value is found. This key/value pair is added to the temp object and the flag is set to true, for later pushing to the result set.
Proceed with the keys of the object.
Later check push and push temp object with hierarchy property and custom properties to the result set.
function getFlat(object) {
var stack = [[object, { hierarchy: [] }]],
result = [],
temp, o, push;
while (stack.length) {
[o, temp] = stack.shift();
push = false;
Object.keys(o).forEach(k => {
if (o[k] && typeof o[k] === 'object' && !Array.isArray(o[k])) {
stack.push([o[k], { hierarchy: temp.hierarchy.concat(k) }]);
} else {
temp[k] = o[k];
push = true;
}
});
push && result.push(temp);
}
return result;
}
var data = [{ hierarchy: ["obj1"], prop1: "value" }, { hierarchy: ["obj1", "obj2"], prop2: "value", prop3: "value" }, { hierarchy: ["obj1", "obj3"], prop4: "value", prop5: "value" }, { hierarchy: ["obj1", "obj3", "obj4"], prop6: "value", prop7: "value", arr: ["val1", "val2"] }],
object = data.reduce((r, { hierarchy, ...rest }) => {
var last = hierarchy.pop();
hierarchy.reduce((o, k) => o[k] = o[k] || {}, r)[last] = rest;
return r;
}, {}),
reclaimedData = getFlat(object);
console.log(object);
console.log(reclaimedData);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories