I have a JSON object like so, (this is an example)
house.keys = {
"key1" : "23456",
"key2" : "97543",
"key3" : "35493"
}
house.doors = [{
"name" : "Front door",
"lock key" : house.keys.key1
}, {
"name" : "Back door",
"lock key" : house.keys.key2
}
]
This object is stored in the DB as a string, and read and parsed() on page load. It will likely be edited by user interaction, then the altered object needs to be stringify()-ed and written back into DB. I've got everything working except for a problem in the stringify() function.
Instead of this:
"lock key" : house.keys.key1
I get this:
"lock key" : "23456"
How do I get the stringify() method to return the referenced object's name instead of the referenced object, itself.
I've looked into using a replacer function:
JSON.stringify(house, replacer); // Where replacer() returns 'something else' for "lock key", but I don't know what 'something else' is.
Thanks.
Just enclose the name in quotes so that it will be treated as a string and therefore, the value will not be parsed and put.
house.doors = [{
"name": "Front door",
"lock key": "house.keys.key1"
}, {
"name": "Back door",
"lock key": "house.keys.key2"
}]
This does appear to be the best solution to my problem. It did require a change to the code that was using the 'house' object.
Before, I was referencing the key as such:
var key1 = house.doors[0]["lock key"];
Now I have to use:
var key1 = house.doors[0]["lock key"];
var key1 = eval(key1);
(I believe I've written this right. My actual code is considerably different.)
All in all, an easier fix then the other solutions I was contemplating.
Related
I am creating this JSON object
{
"attachment": {
"type": "template",
"payload": {
"template_type": "generic",
"image_aspect_ratio": "square"
"elements": [
new CreateBallon(results[rand])
]
}
}
}
I want to set the image_aspect_ratio in only if elements[0].someProp is present.
How can I do that?
Let's clear what JSON is. It is a textual notation for data. If you do anything with the object that involves code of Javascript don't call it JSON so, The object you're talking about is not a JSON but a JavaScript object created via object literal notation.
Now to answer your query, you can't refer to this object before you've fully initialized that object. So what you can do is initialize your object with some default value and then you should be able to edit any property based on the properties from the same object. Something like
var obj = {"property1" : "X", "property2" : ""};
Now you can update your object like
obj.property2 = obj.property1 =='yourCondition' ? 'NewValue' : obj.property2
I have been unable to reach into my MongoDB collection and change a value in a complex document. I have tried more variations than the one example shown below, all sorts of variations, but they fail.
I want to change the Value of the Key "air" from "rain" to "clear". In real life, I will not know that the current Value of the Key "air" is "rain".
Note, I am not using the MongoDB _id Object and would like to accomplish this without using it.
3 documents in the weatherSys collection:
{
"_id" : ObjectId("58a638fb1831c61917f921c5"),
"SanFrancisco" : [
{ "sky" : "grey" },
{ "air" : "rain" },
{ "ground" : "wet" }
]
}
{
"_id" : ObjectId("58a638fb1831c61917f921c6"),
"LosAngeles" : [
{ "sky" : "grey" },
{ "air" : "rain" },
{ "ground" : "wet" }
]
}
{
"_id" : ObjectId("58a638fb1831c61917f921c7"),
"SanDiego" : [
{ "sky" : "grey" },
{ "air" : "rain" },
{ "ground" : "wet" }
]
}
var docKey = "LosAngeles";
var subKey = "air";
var newValue = "clear";
var query = {};
//var queryKey = docKey + ".$";
query[query] = subKey; // query = { }
var set = {};
var setKey = docKey + ".0." + subKey;
set[setKey] = newValue; // set = { "weather.0.air" : "clear" }
db.collection('weatherSys').update(query, { $set: set }, function(err, result) {
if (err) throw err;
});
UPDATE-1:
Ok, so I was hoping I could find a layout a bit simpler than you had suggested but I failed. Everything I tried was not addressable at the "air" Key level. So I copy and pasted your exact JSON into my collection and ran it. I'm using MongoChef to manipulate and test the collection.
Here is my new layout drived from pasting your JSON in 3 times to create 3 documents:
When I then attempted to update the "San Francisco" document's "air" key I got an unexpected result. Rather than updating "air":"dry" it created a new "air" key in the "San Francisco" Object:
So I thought ok, lets try the update again and see what happens:
As you can see it updated the "air" key that it had previously created. I could fight this out and try to make it work "my" way but I just want it to work so I reconfigure my collection layout again, along the lines of what is "working":
And run the update again:
Then I verify it by running the update again:
It works, I am updating properly in a multi-document environment. So this is my current working collection layout:
I have a couple of questions about this-
I am using the top level Key "weather" in every document. It adds nothing to the information within the document. Is there a layout design change that would not necessitate that Key and the overhead it brings along?
Lets say I have to use the "weather" key. Its value is an array, but that array only has one element, the Object which contains the Keys: city, sky, air, and ground. Does addressing necessitate the use of an array with only one element? Or could I get rid of it. Instead of "weather":[{}] could the design be "weather":{} or would I get into non addressability issues again?
It appears I can now update() any of the Values for the Keys: air, sky, and ground, but what is the find() structure to say READ the Value of the Key "ground" in one of the documents?
----> OK, I think I've got this question #3-
db.weatherSys.find({ "weather.city" : "San Francisco" }, { "weather.ground": 1 })
In the original collection layout that you had suggested, could you explain to me why it did not update as you and I had expected but instead created a new the "city" object?
A lot here. I appreciate your sticking with it.
You can't use positional operator for querying the array by its key.
You can access the weather array by index, but that means you know the array index.
For example if you want to update air element value in weather array.
db.collection('weatherSys').update( {}, { $set: { "weather.1.air" : "clear"} } );
Update:
Unfortunately, I can't see any way to update the values without knowing the array index for key.
You don't need query object as your keys are unique .
db.collection('weatherSys').update( {}, { $set: { "SanFrancisco.1.air" : "clear"} } );
or
Other variant if you want to make sure the key exists.
db.collection('weatherSys').update( { "SanFrancisco": { $exists: true } }, { $set: { "SanFrancisco.1.air" : "clear"} } );
Not sure if you can but if you can update your structure to below.
{
"_id" : ObjectId("58a638fb1831c61917f921c5"),
"weather" : [
{
"city": "LosAngeles",
"sky" : "grey" ,
"air" : "rain" ,
"ground" : "wet"
}
]
}
You can now use $positional operator for update.
db.collection('weatherSys').update( {"weather.city":"LosAngeles"}, { $set: { "weather.$.air" : "clear"} } );
I am using the top level Key "weather" in every document. It adds
nothing to the information within the document. Is there a layout
design change that would not necessitate that Key and the overhead it
brings along?
The only layout that I can think of is promoting all the embedded properties to the top level. Sorry, not sure why I didn't think of this the first time around. Sometimes you just need a right question to get the right answer.
{
"_id" : ObjectId("58a638fb1831c61917f921c5"),
"city": "LosAngeles",
"sky" : "grey",
"air" : "rain",
"ground" : "wet"
}
All the updates will be simply top level updates.
db.collection('weatherSys').update( {"city":"LosAngeles"}, { $set: { "air" : "clear"} } );
Lets say I have to use the "weather" key. Its value is an array, but
that array only has one element, the Object which contains the Keys:
city, sky, air, and ground. Does addressing necessitate the use of an
array with only one element? Or could I get rid of it. Instead of
"weather":[{}] could the design be "weather":{} or would I get into
non addressability issues again?
N/A if you are okay with first suggestion.
It appears I can now update() any of the Values for the Keys: air,
sky, and ground, but what is the find() structure to say READ the
Value of the Key "ground" in one of the documents?
db.weatherSys.find({ "city" : "San Francisco" }, { "ground": 1 })
In the original collection layout that you had suggested, could you
explain to me why it did not update as you and I had expected but
instead created a new the "city" object?
That is a copy paste error. I meant to suggest the working layout you have right now. Updated my previous layout.
I have an array of javascript object that looks like the following.
jsObjFromCsv =
[
{
"J251525" : {
"APPROVER" : "TOM#MAIL.COM",
"JOB DESCRIPTION " : "CLEAN THE HOUSE",
"JOB NUMBER" : "J251525"
}
}, {
"J512912" : {
"APPROVER" : "JAMES#MAIL.COM",
"JOB DESCRIPTION " : "BRUSH HORSE",
"JOB NUMBER" : "J512912"
}
}, {
"J5-512" : {
"APPROVER" : "HARRY#MAIL.COM",
"JOB DESCRIPTION " : "WASH CAR",
"JOB NUMBER" : "J5-512"
}
}
]
However, when I save to firebase using the following code it looks like this
saveJobToFirebase(jobs: Array<Object>) {
const jobCodesRef = this.af.database.list('/jobCodes/' + this.currentUser.company)
return jobCodesRef.push(jobs);
}
I want to get rid of the 0,1,2 such that I can query end point like
jobCodes/Company1/-Kc8Q5Wuq4M91puQ_70J/J251525
-----------------My Attempt--------------
I have thought of a way that works but it does not seem to be good as explained at the end of this.
So to achieve what I wanted, I firstly change my object array to be the following
[
{
"APPROVER" : "TOM#MAIL.COM",
"JOB DESCRIPTION " : "CLEAN THE HOUSE",
"JOB NUMBER" : "J251525"
}, {
"APPROVER" : "JAMES#MAIL.COM",
"JOB DESCRIPTION " : "BRUSH HORSE",
"JOB NUMBER" : "J512912"
}, {
"APPROVER" : "HARRY#MAIL.COM",
"JOB DESCRIPTION " : "WASH CAR",
"JOB NUMBER" : "J5-512"
}
]
Then I loop through each of the object and grab a job number to get the end point and directly "SET" it to firebase with the following code
saveJobToFirebase(jobs: Array<Object>) {
// previous code
// const jobCodesRef = this.af.database.list('/jobCodes/' + this.currentUser.company)
// return jobCodesRef.push(jobs);
// bad attempt?
for (let job of jobs) {
const jobCodesRef = this.af.database.object('/jobCodes/' + this.currentUser.company + '/' + job['JOB NUMBER']).set(job);
}
}
And this gives me the result that I wanted.
However, there are two big problems with this method
my saveJobToFirebase no longer returns a thenableReference for me to call .then at my Component. This means that I would have no way to track whether or not the action succeeded
I dont know if updating firebase with for loop is a good idea? What if this JSON object has 2000 entries... I would be hammering the end point if I call it inside a for loop. It would be better if I can "push" it so that everything goes in with one request right?
The 0, 1, 2, etc are created because you're saving an array of objects. See this blog post about arrays in Firebase for more on why this behavior exists and why Firebase recommends against storing arrays.
Calling push() will generate a so-called push ID, a value that Firebase guarantees to be unique. But since your jobs already have their own ID, this isn't needed either.
The structure you want to save, seems better: the objects each have a usable key. You could save this object with:
jsObjFromCsv = {
"J251525" : {
"APPROVER" : "TOM#MAIL.COM",
"JOB DESCRIPTION " : "CLEAN THE HOUSE",
"JOB NUMBER" : "J251525"
},
"J512912" : {
"APPROVER" : "JAMES#MAIL.COM",
"JOB DESCRIPTION " : "BRUSH HORSE",
"JOB NUMBER" : "J512912"
},
"J5-512" : {
"APPROVER" : "HARRY#MAIL.COM",
"JOB DESCRIPTION " : "WASH CAR",
"JOB NUMBER" : "J5-512"
}
};
If you watch carefully, you'll see that I've removed the array and the outermost level of objects.
Now you can save this object with:
const jobCodesRef = this.af.database.list('/jobCodes/' + this.currentUser.company)
jobCodesRef.update(jsObjFromCsv);
The return values from update() is thennable, so you can continue when the action has completed (or failed).
For anyone interested, try to transform your array of objects to an object, with just a few lines of reduce
const arrayToObject = (array) =>
array.reduce((obj, item) => {
obj[item.id] = item
return obj
}, {})
const jsObjFromCsv_OBJ = arrayToObject(jsObjFromCsv)
I had similar problem but in node and no answers proved sufficient. I know your code is different but the principle is the same. Hopefully this helps someone in the future. First Firebase can save data in different ways like push or set. Push when using arrays with square brackets automatically adds in those numbers such as 0, 1, 2 so that say two people post on a blog at the same time they don't overwrite each other. Set ignores this and overwrites everything at the path to save the data as you have specified.
In my case I was adding json objects to an array with .push and getting the 0, 1, 2 and I wanted names instead of 0, 1, 2 so I basically just switched the array with an object and instead of using .push I set objects inside the one big object. To illustrate this:
What I used first:
var array = [];
jsonobject={
title:'title'
}
array.push(jsonobject);
//then I used push to firebase that array in an advanced method
The fix:
var mybigjson = {};
var nameiwantinsteadofnumbers = [insert your changing variable for each jsonobject here]
jsonobject={
title:'title'
}
mybigjson[nameiwantinsteadofnumbers]=jsonobject
//then I used push to firebase that object in an advanced method
I want to merge particular part of a JSON code with another JSON. To clarify, please refer to example below:
JSON 1:
{"metadata": {
"userName": "Batman",
"userId" : "402",
"config":{
"a" : "some text here",
"b" : "some other text here"
}
}}
JSON 2:
{"metadata": {
"userName": "Superman",
"userId" : "102",
"config":{
"a" : "Overwrite Text 1",
"b" : "Overwrite Text 2"
}
}}
Now, I want to overwrite ONLY config part JSON 1 with JSON 2 So that JSON 1 will become:
{"metadata": {
"userName": "Batman",
"userId" : "402",
"config":{
"a" : "Overwrite Text 1",
"b" : "Overwrite Text 2"
}
}}
I have already tried using loop but I want to figure out if there is more efficient way to code this up. I am using Angular and pure JS.
You can try Object.assign - Mozilla foundation doc
var obj1 = JSON.parse(json1);
var obj2 = JSON.parse(json2);
Object.assign(obj1.metadata.config,obj2.metadata.config);
Its really simple:
json1.metadata.config = json2.metadata.config;
You overwrite its config data with the config from the other json object.
Let me know if you want me to elaborate more.
json1.metadata.config = json2.metadata.config;
should be enough.
You've got to remember that modifiying json2.metadata.config will modify json1.metadata.config, this is a shallow copy.
Using Object.assign as Djave said is correct, but if you're not using ES6 and a transpiler you can take advantage of Angular itself and do a plain angular.copy:
var obj1 = JSON.parse(json1);
var obj2 = JSON.parse(json2);
obj1.metadata.config = angular.copy(obj2.metadata.config);
This way you will get the new JSON part copied and referenced on its own.
I need to pull the value of property "title"
KV = {
clientPath: '/0000000000/client',
serverPath: '',
application: '/00000000/client/application/player.js',
properties: '/000000000/client/custom-config/AppProperties.js',
pollingEnabled: false,
customerConfig: {},
presentationTypeConfig: {},
kuluConfig: {},
kulu: {
"guid" : "XXXXXXX",
"title" : "XXXXX",
"createdInApp" : false,
"allowFeedback" : true,
"publisher" : {
"id" : 000000001,
"username" : "XXXXXXX",
"name" : "XXXXXXXX"
},
I tried looping but I just get returned undefined.
I have no access to the code to change it.
Have you tried this?
KV.kulu.title
first of all: the json you have posted is invalid. publisher-property has only an opening curly bracket.
second: here's a working fiddle, with valid json and just the code kv.kulu.title which does exactly what you are (literally) asking for:
http://jsfiddle.net/k75cxdkh/1/
edit: I'm just guessing here, but re-reading your question and json code, it seems you try to loop over an array of objects to get a nested object by it's value dynamically. When trying this, do it e.g. like this (using underscorejs):
var arr = _.filter(KV, function(obj) {
return _.some(obj.kulu, {id: ID_TO_FIND});
});
if not, nevermind. It's just a bit weird you are asking for a such common task.
quick and easy: KV.kulu.title no loop required!