I have an object and I am iterating through it's properties in order to change them (I want to replace 'a.1'-kind to 'a[1]'-kind):
.fail(function (data) {
var errors = data.responseJSON;
console.log("Object before: ", errors);
console.log("Changed properties:")
for (var property in errors) {
if (errors.hasOwnProperty(property)) {
if (property.includes('.')) {
property = property.replace(/\./, "[");
property = property.replace(/$/, "]");
console.log(property);
}
}
}
console.log("Object after: ", errors);
The properties change during iteration, but object's properties don't change for real:
How do I changed object's properties not only while iterating through them, but "forever"?:)
Appreciate any help:)
You may delete and reassign:
if (property.includes('.')) {
errors[property.replace(/\./, "[").replace(/$/, "]")]=errors[property];
delete errors[property];
}
You may ask why does
property=property.replace(..);
Not work?
Well property is not related to object in any way. Its just a string...
Like the post from #Jonas w above, you can do a delete and reassign the value.
Another example here (does not include your string replacement/regex logic, but shows how you can update/alter the keys:
let logger = document.querySelector('pre');
let obj = {
foo: 'foo-value',
bar: 'foo-value',
baz: 'foo-value',
qux: 'foo-value'
};
logger.innerHTML = `Original: ${JSON.stringify(obj, null, 2)}\n`;
Object.keys(obj).forEach((oldKey) => {
let newKey = oldKey + '-new';
let originalVal = obj[oldKey];
obj[newKey] = originalVal;
delete obj[oldKey];
});
logger.innerHTML += `Updated: ${JSON.stringify(obj, null, 2)}\n`;
<pre></pre>
A functional approach:
function replaceProperties(obj) {
var newObj = Object.getOwnPropertyNames(obj).reduce(function (newObj, prop) {
var newProp = prop;
if (prop.includes('.')) {
newProp = prop.replace(/\./, "[").replace(/$/, "]");
}
newObj[newProp] = obj[prop];
return newObj;
}, {});
return newObj;
}
var newObj = replaceProperties(errors);
Related
I'm trying to figure out a way to turn and object like this :
{ "test.subtest.pass" : "test passed", "test.subtest.fail" : "test failed" }
into JSON like this:
{ "test": { "subtest": { "pass": "test passed", "fail": "test failed" }}}
sometimes there may be duplicate keys, as above perhaps there would be another entry like "test.subtest.pass.mark"
I have tried using the following method and it works but it's incredibly ugly:
convertToJSONFormat() {
const objectToTranslate = require('<linkToFile>');
const resultMap = this.objectMap(objectToTranslate, (item: string) => item.split('.'));
let newMap:any = {};
for (const [key,value] of Object.entries(resultMap)) {
let previousValue = null;
// #ts-ignore
for (const item of value) {
// #ts-ignore
if (value.length === 1) {
if(!newMap.hasOwnProperty(item)) {
newMap[item] = key
} // #ts-ignore
} else if (item === value[value.length - 1]) {
if(typeof previousValue[item] === 'string' ) {
const newKey = previousValue[item].toLowerCase().replace(/\s/g, '');;
const newValue = previousValue[item];
previousValue[item] = {};
previousValue[item][newKey] = newValue;
previousValue[item][item] = key;
} else {
previousValue[item] = key;
}
} else if (previousValue === null) {
if (!newMap.hasOwnProperty(item)) {
newMap[item] = {};
}
previousValue = newMap[item];
} else {
if (!previousValue.hasOwnProperty(item)) {
previousValue[item] = {}
previousValue = previousValue[item];
} else if (typeof previousValue[item] === 'string') {
const newValue = previousValue[item];
previousValue[item] = {};
previousValue[item][item] = newValue;
} else {
previousValue = previousValue[item];
}
}
}
}
return newMap;
}
We can utilize recursion to make the code a little less verbose:
function convertToJSONFormat(objectToTranslate) {
// create root object for the conversion result
const result = {};
// iterate each key-value pair on the object to be converted
Object
.entries(objectToTranslate)
.forEach(([path, value]) => {
// utilize a recursive function to write the value into the result object
addArrayPathToObject(result, path.split("."), value);
});
return result;
}
function addArrayPathToObject(root, parts, value) {
const p = parts.shift();
// base-case: We attach the value if we reach the last path fragment
if (parts.length == 0) {
root[p] = value
return;
}
// general case: check if root[p] exists, otherwise create it and set as new root.
if(!root[p]) root[p] = {};
addArrayPathToObject(root[p], parts, value)
}
This function utilizes the fact that objects are pass-by-reference to recursively traverse through the object starting at its root until setting the desired value.
You can add error-handling and other such concerns as necessary for your use.
#Meggan Naude, toJson function copies json object to reference obj for provided keys and value.
const p = { "test.subtest.pass" : "test passed", "test.subtest.fail" : "test failed" };
const result = {} ;
const toJson = (obj, keys, value) => {
if (keys?.length === 1) {
obj[keys[0]] = value;
return obj
} else {
const k = keys.splice(0, 1)
if (k in obj) {
toJson(obj[k], keys, value)
} else {
obj[k] = {};
toJson(obj[k], keys, value)
}
return obj
}
}
Object.keys(p).forEach(key => toJson(result, key.split('.'), p[key]))
console.log(result);
function renameKeys(obj, newKeys) {
const keyValues = Object.keys(obj).map((key) => {
let newKey = key + "1";
if (Array.isArray(obj[key]) == false) {
renameKeys(obj[key], newKeys);
}
console.log(newKey, "]", obj[key]);
return {
[newKey]: obj[key],
};
});
return Object.assign({}, ...keyValues);
}
test = JSON.parse(
'{"verifying_explanation":
{"bus_stop":["1234"],
"elementary_school":["1234"],
"middle_school":["1234"],
"high_school":["1234"]
}
}'
);
console.log(test);
data = renameKeys(test, this);
console.log(data);
It look like all keys changed in function, but it is not applied . I think because of copy principal.
I have no idea how I can manipulate for keys.
I want to replace all keys so that I apply i18n in my code.
So new key will be somethign like
let newKey = i18n.$t(key);
This short code is just for test code.
Please give me some ideas to solve this problem.
You need to define your function to create new key value pairs and then form an object from these. Also, check if the value is an object, to recursively rename nested objects -
function renameKeys(obj) {
const keyValues = Object.entries(obj).map(([key, value]) => {
let newKey = key + "1";
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
value = renameKeys(value);
}
return [newKey, value];
});
return Object.fromEntries(keyValues);
}
test = JSON.parse(
'{"verifying_explanation": {"bus_stop": ["1234"],"elementary_school": ["1234"],"middle_school": ["1234"],"high_school": ["1234"]}}'
);
console.log(test);
data = renameKeys(test, this);
console.log(data);
You can't return new key-value pair in your function, instead of that, you just need to add new key to obj and delete old one.
function renameKeys(obj, newKeys) {
Object.keys(obj).map((key) => {
let newKey = key + "1";
if (Array.isArray(obj[key]) == false) {
renameKeys(obj[key], newKeys);
}
// console.log(newKey, "]", obj[key]);
obj[newKey]=obj[key];
delete obj[key];
});
}
test = JSON.parse(
`{"verifying_explanation":
{"bus_stop":["1234"],
"elementary_school":["1234"],
"middle_school":["1234"],
"high_school":["1234"]
}
}`
);
console.log(test);
data = renameKeys(test, this);
console.log(test);
I have this function that works in Chrome, which prints to the console when a variable called finishedLoading changes values.
Object.observe(finishedLoading, function(id, oldval, newval) {
console.log('finished loading' + id + ' went from ' + oldval + ' to ' + newval);
}
This doesn't work in a bunch of other modern browsers (e.g. firefox, safari). Is there an alternative I can use that would be better supported? Thanks!
A more widely supported approach could be Object.defineProperty. defineProperty can be used to have some control on a certain property of an object for instance:
var o = { prop: '' };
Object.defineProperty(o, 'prop', {
get: function() { return this.value; },
set: function(newValue) {
// a certain property is being changed
alert('is changed');
this.value = newValue;
}
});
o.prop = 'Johnson';
The above example shows how you can use defineProperty and when prop of object o is altered a defined setter (set) is called.
At the bottom of this reference you can see that even IE-8 supports it but only under certain conditions (IE8 only support Object.defineProperty to be used on DOM nodes).
But be careful when using it because this would assign a property to the window object as well because of a missing this:
var o = { b:''};
Object.defineProperty(o, 'b', {
get: function() { return value; },
set: function(newValue) { value = newValue; },
});
o.b = 'abc';
console.log(window.value); // 'abc'
Way to track old value of a property
This matches more your request:
var o = { prop: '' };
Object.defineProperty(o, 'prop', {
get: function() { return this.propValue; },
set: function(newValue) {
// an certain property is being changed
console.log('old-value: ',this['oldprop']);
console.log('new-value: ',newValue);
this.propValue = newValue;
this['oldprop'] = this.propValue;
}
});
o['prop'] = 'joseph';
console.log(o);
o['prop'] = 'jack';
console.log(o);
o['prop'] = 'john';
console.log(o);
Observe whole Object by using Object.defineProperty
And in addition to that you could make a function that tracks a whole object and whether any property is being changed:
function observeObject(obj){
var keys = Object.keys(obj);
for(var k=0; k < keys.length; k++){
var key = keys[k];
(function(key){
var keyName = key+'value';
var oldKeyName = 'old'+key+'value';
obj[oldKeyName] = obj[key];
Object.defineProperty(obj, key, {
get: function() { return this[keyName]; },
set: function(newValue) {
console.log('old-value: ',this[oldKeyName]);
console.log('new-value: ',newValue);
this[keyName] = newValue;
this[oldKeyName] = this[keyName];
}
});
})(key);
}
}
var person = { name : 'jack', age: 26 };
observeObject(person);
person.name = 'john';
person['age'] = 27;
I wonder if it would be possible for me to employ a recursive function to read all the attributes and properties of an object including the nested properties and such. for example,
if I have an object:
var mObj = {};
mObj.countries = [];
mObj.country = {};
mObj.country.states = [];
mObj.country.state = {};
mObj.country.state = {};
I am sure you get the picture. If it was just a simple object then I can employ "for in" loop, and perhaps nested "for in" loop, an object has numerous nested levels then using nested "for in" loops becomes somewhat chaos. I thought it would wonderful to employ recursion. Any help insight to this would highly appreciated.
Thank you.
Here's a quick example that does this in case you end up running into any problems.
var level = '';
var readProperties = function(val) {
if (Object.prototype.toString.call(val) === '[object Object]') {
for (var propertyName in val) {
if (val.hasOwnProperty(propertyName)) {
console.log(level + propertyName + ':');
level += ' ';
readProperties(val[propertyName]);
}
}
}
else {
console.log(level + val);
level = level.substring(0, level.length - 2);
}
}
Had same problem as OP, but couldn't get the accepted solution working properly. Here's what I ended up doing:
function foreachAttribute(object, handleAttributeName, handleAttributeValue) {
var attributeNames = [];
function recursion(object) {
for ( var attribute in object) {
if (typeof object[attribute] == 'object') {
attributeNames.push(attribute);
recursion(object[attribute]);
attributeNames = attributeNames.slice(0,
attributeNames.length - 1);
} else {
handleAttributeName(attributeNames.join(".") + "."
+ attribute);
handleAttributeValue(object[attribute]);
}
}
}
recursion(object);
}
var attributeName = "";
var handleAttributeName = function(name) {
attributeName = name;
};
var handleAttributeValue = function(value) {
console.log(attributeName + "=" + value);
};
var data = {
var1 : {
asdf : 123
},
var2 : 321
};
foreachAttribute(data, handleAttributeName, handleAttributeValue);
I'm running into a bizarre bug while trying to create a Dictionary object. Pretty basic stuff. However when I create 2 instances of the object, and then set some values on one, they appear on both. What am I doing wrong here?
function Dict() { }
Dict.prototype = {
items: { },
prop: function(key) {
return ':' + key;
},
get: function(key, def) {
var p = this.prop(key),
k = this.items;
return k.hasOwnProperty(p) ? k[p] : def;
},
set: function(key, value) {
var p = this.prop(key);
this.items[p] = value;
return value;
},
count: function() {
return Object.keys(this.items).length;
},
has: function(key) {
var p = this.prop(key);
return this.items.hasOwnProperty(p);
},
del: function(key) {
var p = this.prop(key),
k = this.items;
if(k.hasOwnProperty(p))
delete k[p];
},
keys: function() {
return Object.keys(this.items).map(function(key) {
return key.substring(1);
});
}
};
var a = new Dict();
var b = new Dict();
a.set('foo', 'bar');
console.log(a.keys());
console.log(b.keys());
You're defining items inside your prototype which means it will be shared by all instances. You need to set it inside the "constructor" function and remove it from the prototype.
function Dict() { this.items = []; }
I've created a JS Fiddle for you with the full source code at http://jsfiddle.net/brunomsilva/zaSY2/.
The items property is set on the prototype. The prototype is not cloned when creating an object, so items is the same on the two Dicts. Set items in the constructor so each object has its own:
function Dict() {
this.items = {};
}
Prototypes work because when you try to access an object's property, it first checks the object's own properties to see if it contains it. If so, that's the value. If it's not found there, it checks the prototype. If it's not there, it continues traversing the chain of prototypes until it finds the property. If it's still not found, it results in undefined. (for more detail, see the specification)
to define a class to use, try moving the function definitions to the prototype without replacing the prototype object, like so:
function Dict() {
this.items = {};
}
Dict.prototype.prop = function (key) {
return ':' + key;
};
Dict.prototype.get = function (key, def) {
var p = this.prop(key),
k = this.items;
return k.hasOwnProperty(p) ? k[p] : def;
};
Dict.prototype.set = function (key, value) {
var p = this.prop(key);
this.items[p] = value;
return value;
};
Dict.prototype.count = function () {
return Object.keys(this.items).length;
};
Dict.prototype.has =function (key) {
var p = this.prop(key);
return this.items.hasOwnProperty(p);
};
Dict.prototype.del =function (key) {
var p = this.prop(key),
k = this.items;
if (k.hasOwnProperty(p))
delete k[p];
};
Dict.prototype.keys = function () {
return Object.keys(this.items).map(function (key) {
return key.substring(1);
});
};