I've been trying for a couple hours to do this, most questions and examples I've seen are not addressing my problem, for instance This one here is talking about the keys, not values.
I tired using the JSON parser, but there are two issues:
How do I iterate through the values to begin with? I know there are different ways to read keys, but what about values and nested values (given that we don't know anything about what the keys or values are called).
How to actually write the value and replace it, rather than replacing the whole file, maybe something like:
key.value=key.value.toUpper();
I am looking for a solution that works for any JSON file, with absolultly no knowledge what the keys are called.
You could use replace to operate on the JSON string directly:
jsonString.replace(/"\s*:\s*"[^"]/g, match => {
return match.slice(0, -1) + match[match.length - 1].toUpperCase()
})
That would save you from having to parse the JSON, and might be a bit faster. It can be tough to write a performant comprehensive RegEx though, so it might be safer just to parse the JSON and write a little recursive function:
const uppercaseValues = obj => {
return Object.keys(obj).reduce((uppercased, key) => {
const value = obj[key]
if (typeof value === 'string') {
uppercased[key] = value[0].toUpperCase() + value.slice(1)
} else if (typeof value === 'object') {
uppercased[key] = uppercaseValues(value)
} else {
uppercased[key] = value
}
return uppercased
}, {})
}
const parsedJson = JSON.parse(jsonString)
const uppercased = uppercaseValues(parsedJson)
const xformedJson = JSON.stringify(uppercased)
Related
I have this json
var json = {
"I:5.3": {
"conf": {
"name": "Jack"
}
}
}
and this string
var str = '["I.5.3"]["conf"]'
How can get str value from json like
json["I.5.3"]["conf"]
I have json object and str and I have to extract something like
json[str] = json["I.5.3"]["conf"]
Your title suggests you would like to get:
The value of a string.
Which has a list of Array's.
From a JSON object.
However, all your code shows is a Plain JavaScript object. No arrays to be seen... and the only string value I can find is "Jack".
Jack would be extracted like, json["I.5.3"]["conf"]["name"].
Or via dot notation =>
const obj = json["I.5.3"];
const nameValue = obj.conf.name; // => Jack
You have 2 ways(at least that's what's in coming through my mind now):
1st way
var json = {
"I:5.3": {
"conf": {
"name": "Jack"
}
}
}
var str = '["I:5.3"]["conf"]'
var scr_line = 'json'+str;
var target = eval(scr_line);
console.log(target);
2nd way:
var json = {
"I:5.3": {
"conf": {
"name": "Jack"
}
}
}
var str = '["I:5.3"]["conf"]';
let ret = getVal(json, str);
console.log(ret);
function getVal(obj, path){
var regex = /\["(.*?)"\]/mg;
let m;
while ((m = regex.exec(path)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
if(typeof obj[m[1]] !== 'undefined') obj = obj[m[1]];
else return obj[m[1]];
}
return obj;
}
I would prefer the second because it checks if the property on the object exists
You would have to split the string up into separate parts, I believe.
var str = '["I.5.3"]["conf"]';
var identifiers = str.split('][');
// identifiers would be an array like:
// ["I.5.3", "conf"]
var person = json[identifiers[0]][identifiers[1]];
// person = {
// "name": "Jack"
// }
String.prototype.split() allows you to separate parts of a string out into an array. Checkout the MDN docs to learn more about that particular method.
This specific answer expects that there will always be the properties you split, though and will error out if one is missing. For something a little safer, I would suggest checking to see if that object contains the property you're expecting before trying to access it.
var str = '["I.5.3"]["conf"]';
var identifiers = str.split('][');
try {
var myVal;
// Let's check if the property exists.
if (Object.prototype.hasOwnProperty.call(json, identifiers[0]){
myVal = json[identifiers[0]];
if (Object.prototype.hasOwnProperty.call(myVal, identifiers[1]){
myVal = myVal[identifiers[1]];
}
}
} catch(error) {
// One of the properties didn't exist or something went wrong in the try
// block above.
}
EDIT:
The follow would format your string specifically to meet the match to become an array. If there were single quotes or double quotes anywhere in each array item, this would fall through, so be aware of that.
var myArray = str.slice(2, -2).split('][').map(function(item) {
return item.replace('"', '');
});
String.prototype.slice() extracts a section of a string and returns it as a new string, without modifying the original string.
Then the split method separates it into different array items. Then we iterate over each item in the array and remove additional ". Just to say again, this will fall apart if the original string looks like ['one']['two']. This would also not be reliable if the string looks like ["can't"]["won't"]. So just be aware of that in your particular case. If you're positive that the string will always meet the format you have above, then you can rely on this.
So I'm in a unique situation where I have two objects, and I need to compare the keys on said objects to make sure they match the default object. Here's an example of what I'm trying to do:
const _ = require('lodash');
class DefaultObject {
constructor(id) {
this.id = id;
this.myobj1 = {
setting1: true,
setting2: false,
setting3: 'mydynamicstring'
};
this.myobj2 = {
perm1: 'ALL',
perm2: 'LIMITED',
perm3: 'LIMITED',
perm4: 'ADMIN'
};
}
}
async verifyDataIntegrity(id, data) {
const defaultData = _.merge(new DefaultObject(id));
if (defaultData.hasOwnProperty('myoldsetting')) delete defaultData.myoldsetting;
if (!_.isEqual(data, defaultData)) {
await myMongoDBCollection.replaceOne({ id }, defaultData);
return defaultData;
} else {
return data;
}
}
async requestData(id) {
const data = await myMongoDBCollection.findOne({ id });
if (!data) data = await this.makeNewData(id);
else data = await this.verifyDataIntegrity(id, data);
return data;
}
Let me explain. First, I have a default object which is created every time a user first uses the service. Then, that object is modified to their customized settings. For example, they could change 'setting1' to be false while changing 'perm2' to be 'ALL'.
Now, an older version of my default object used to have a property called 'myoldsetting'. I don't want newer products to have this setting, so every time a user requests their data I check if their object has the setting 'myoldsetting', and if it does, delete it. Then, to prevent needless updates (because this is called every time a user wants their data), I check if it is equal with the new default object.
But this doesn't work, because if the user has changed a setting, it will always return false and force a database update, even though none of the keys have changed. To fix this, I need a method of comparing the keys on an object, rather any the keys and data.
That way, if I add a new option to DefaultObject, say, 'perm5' set to 'ADMIN', then it will update the user's object. But, if their object has the same keys (it's up to date), then continue along your day.
I need this comparison to be deep, just in case I add a new property in, for example, myobj1. If I only compare the main level keys (id, myobj1, myobj2), it won't know if I added a new key into myobj1 or myobj2.
I apologize if this doesn't make sense, it's a very specific situation. Thanks in advance if you're able to help.
~~~~EDIT~~~~
Alright, so I've actually come up with a function that does exactly what I need. The issue is, I'd like to minify it so that it's not so big. Also, I can't seem to find a way to check if an item is a object even when it's null. This answer wasn't very helpful.
Here's my working function.
function getKeysDeep(arr, obj) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object') {
arr = getKeysDeep(arr, obj[key]);
}
});
arr = arr.concat(Object.keys(obj));
return arr;
}
Usage
getKeysDeep([], myobj);
Is it possible to use it without having to put an empty array in too?
So, if I understand you correctly you would like to compare the keys of two objects, correct?
If that is the case you could try something like this:
function hasSameKeys(a, b) {
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
return aKeys.length === bKeys.length && !(aKeys.some(key => bKeys.indexOf(key) < 0));
}
Object.keys(x) will give you all the keys of the objects own properties.
indexOf will return a -1 if the value is not in the array that indexOf is being called on.
some will return as soon as the any element of the array (aKeys) evaluates to true in the callback. In this case: If any of the keys is not included in the other array (indexOf(key) < 0)
Alright, so I've actually come up with a function that does exactly what I need. The issue is, I'd like to minify it so that it's not so big. Also, I can't seem to find a way to check if an item is a object even when it's null.
In the end, this works for me. If anyone can improve it that'd be awesome.
function getKeysDeep(obj, arr = []) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && !Array.isArray(obj[key]) && obj[key] !== null) {
arr = this.getKeysDeep(obj[key], arr);
}
});
return arr.concat(Object.keys(obj));
}
getKeysDeep(myobj);
From node.js documentation
querystring.parse('foo=bar&baz=qux&baz=quux&corge')
// returns
{ foo: 'bar', baz: ['qux', 'quux'], corge: '' }
When I parse for the parameters on the server side, is there a way I can check if these parameters are sent in a correct sequence?
For example, I want to enforce that the foo=bar is always the FIRST parameter and so on. How is this achievable?
Right now I can do something like:
var queryData = querystring.parse('foo=bar&baz=qux&baz=quux&corge');
But then doing
if(queryData.foo) {...}
only checks if the parameter 'foo' is present in the url, not if it's in the right place
You can either parse it yourself with something like this:
var qs = 'foo=bar&baz=qux&baz=quux&corge';
var parsed = qs.split('&').map(function (keyValue) {
return keyValue.split('=');
});
Which will give you something like [ ['foo','bar'], ['baz', 'qux'], ['baz', 'quux'], ['corge', '' ], then you can check if the first is what you want or not: if (parsed[0][0] === 'foo')
You can also check this question for parsing it yourself: How can I get query string values in JavaScript?
Or you can rely on the fact that nodejs will insert the property in the same order it was received and v8 will keep the order(which is not really reliable, they might change the behavior): if (Object.keys(queryData)[0] === 'foo').
Or you can just check if foo is appeared first:
if (qs.indexOf('foo=') === 0)
or something like if (qs.slice(0, 4) === 'foo')
You might be better of doing an old fashion string.split since it gives you an array of split elements. You can then inspect the array
How do I split a string, breaking at a particular character?
check if this code works for you:
var querystring = require("querystring");
var obj = querystring.parse('foo=bar&baz=qux&baz=quux&corge');
var rankOfObjects = {"0":"foo","1":"baz","2":"corge"};
var objArray = Object.keys(obj);
if(checkRank()){
console.log("in order");
}else{
console.log("not in order");
}
function checkRank(){
for(var rank in objArray){
if(objArray[rank]!=rankOfObjects[rank]){
return false;
}
}
return true
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Accessing nested JavaScript objects with string key
I have the function
function _get(name) {
return plugin._optionsObj[name] !== undefined ?
plugin._optionsObj[name] : plugin._defaults[name];
}
I would like to be able to have objects inside of my _defaults object, but then I don't know how to retrieve them but using just one set of square brackets.
i.e.
plugin._defaults = {
val1: 1,
val2: 2,
obj1: {
someVal: 3
}
}
Is it possible to access 'someVal' from the function I have above? I tried passing 'obj1.someVal' for the argument and it didn't work. Ideas?
Edit: I have found a solution and I posted it below as an answer. I've written a very nice little function to do go through the nested values with a string and I didn't have to change my function much to implement it. I hope this helps anyone in a similar situation.
I suspect that you won't always have a one-level nested object to access, so the cleaner way to do this is to use a function that traverses an object based on a string path. Here's one that is coded as a mixin for Underscore. You can then just use it like so:
_.deep(plugin._defaults, 'obj1.someVal');
This thread also has some non-Underscore alternatives.
Pass multiple arguments, and iterate over the arguments object.
function _get(/* name1, name2, namen */) {
var item = plugin._optionsObj,
defItem = plugin._defaults;
for (var i = 0; i < arguments.length; i++) {
item = item[arguments[i]];
defItem = defItem[arguments[i]];
if (item == null || defItem == null)
break;
}
return item == null ? defItem : item;
}
var opt = _get("obj1", "someVal")
I found a solution for this problem, at least one that will accommodate myself, and I'd like to share it in case it can help someone else with this problem. My biggest difficulty is that I did not know the depth of the nested value so I wanted to find a solution that would work for deeply nested objects and without requiring to redesign anything.
/* Retrieve the nested object value by using a string.
The string should be formatted by separating the properties with a period.
#param obj object to pass to the function
propertyStr string containing properties separated by periods
#return nested object value. Note: may also return an object */
function _nestedObjVal(obj, propertyStr) {
var properties = propertyStr.split('.');
if (properties.length > 1) {
var otherProperties = propertyStr.slice(properties[0].length+1); //separate the other properties
return _nestedObjVal(obj[properties[0]], otherProperties); //continue until there are no more periods in the string
} else {
return obj[propertyStr];
}
}
function _get(name) {
if (name.indexOf('.') !== -1) {
//name contains nested object
var userDefined = _nestedObjVal(plugin._optionsObj, name);
return userDefined !== undefined ? userDefined : _nestedObjVal(plugin._defaults, name);
} else {
return plugin._optionsObj[name] !== undefined ?
plugin._optionsObj[name] : plugin._defaults[name];
}
}
To retrieve objects inside of your _defaults object you'll need to improve your _get function.
For example you may pass an array of strings (each string representing a propery name) to _get to allow access to deeply nested objects.
I'm trying to write a system where the client (browser-based) requests a tree to be returned from the server (app engine, but that's irrelevant). The problem is in converting the tree to JSON: because each object refers to the objects 'below' it, when stringified I end up with an extremely long string which, on parsing, creates new objects for each child instead of references to other nodes.
My current solution would be to write an 'equalTo' and 'toString' function (converting object references to strings) stringify the resulting array and then recreate it on the client side by resolving strings to objects. That solution is making my terrible-algorithm-senses tingle, though, there must be a better way to return such structures through JSON!
EDIT: it just occurred to me that object references could also be converted to array indexes. It's a better solution, but still has that niggling bad-code feel to it.
EDIT2: Right, so I suppose some pseudo-code is in order, then.
var node = {
children : null;
};
var root = Object.create(node);
var level1a = Object.create(node);
var level1b = Object.create(node);
var level2a = Object.create(node);
var level2b = Object.create(node);
root.children = [level1a, level1b];
level1a.children = [level2a, level2b];
So you end up with a tree that looks like this:
stackoverflow won't let me post images
If you have a way to address nodes, you can use JSON revivers and replacers to convert between addresses and references.
For example, if you have two functions like
function addressForNode(node) { ... }
function nodeForAddress(address) { ... }
you could use revivers and replacers that call those when parsing or stringifying
var jsonString = JSON.stringify(tree, function (key, value) {
if (typeof value === 'object') {
var address = addressForNode(value);
if (address !== null) { // value really is a node
return { address: address }
}
}
return value;
});
// And to parse...
var tree = JSON.parse(jsonString, function (key, value) {
if (typeof value === 'object' && typeof value.address === 'string') {
return nodeForAddress(value.address);
}
return value;
});
If you can help it, don't restrict yourself to JSON. Instead consider generating JavaScript code included via linked script, using JSONP techniques if necessary.