I'm trying to create a function that when called will update a specific object in json file. However, it updates the object as well as creating a new one.
I've tried many different methods in trying to get this to work, but all have failed. The closest I've got to it working is the code shown below, but it still doesn't do what is required.
This is my function:
var fs = require('fs');
var _ = require("underscore");
module.exports = {
personalUpdate: function (id, forename, surname, dob, gender, callback) {
let rawdata = fs.readFileSync('data.json');
let data = JSON.parse(rawdata);
let filtered = _.where(data['students'], { id: id });
let all = filtered[0];
all.forename = forename;
all.surname = surname;
all.dob = dob;
all.gender = gender;
data["students"].push(all);
fs.writeFileSync('data.json', JSON.stringify(data, null, 2), (err) => {
if (err) throw err;
});
callback("success");
}
}
And this is the JSON file that I want to update:
{
"teachers": [
{
"name": "",
"email": "",
"password": "",
"formGroup": "",
"id": ""
}
],
"students": [
{
"surname": "test",
"forename": "test",
"dob": "",
"homeAddress": "",
"homePhone": "",
"gender": "",
"tutorGroup": "",
"schoolEmail": "",
"grades": [
{
"french": 8,
"maths": 7
}
],
"id": ""
},
{
"surname": "test2",
"forename": "test2",
"dob": "",
"homeAddress": "test2",
"homePhone": "",
"gender": "",
"tutorGroup": "",
"schoolEmail": "",
"grades": [
{
"french": 9,
"maths": 8
}
],
"id": ""
}
]
}
I had to remove and change the objects and info inside them, as it contained confidential information.
When running this function, it finds the object that is specified in the parameter. It then updates that object, but it then creates another object at the bottom of the original JSON object, which it is not supposed to.
Also, is there a better way to update the specific objects in the JSON file?
tl;dr
The result set is duplicating because you are pushing it into the array
The change is being applied due to the variables holding the same object reference, so they are being mirrored across objects that share the same pointer.
Explanation
It creates a new one due to the data["students"].push(all); instruction.
When you manipulate objects in javascript you need to be aware of how the reference between them work, so you can avoid bugs and use them in your benefit.
For example, take this set of instructions:
let a = {"x": 1};
let b = a;
b.x = 3;
console.log(a) // it will output {"x": 3}
Notice that we:
Create an object with the prop x equal 1 and assign it to the variable a
Initialize a variable b with the value of a
Change the property x on the variable/object b
Then we can observe that the change was also reflected in the variable a, due to the object reference.
So, basically this is exactly what is happening with your instructions when you do all.forename = forename; it changes the variable all, but also the original object which it derives from.
Here is a nice reference that explains this concept more in-depth
#EDIT
I strongly advise you not using the sync version of functions like readFileSync since this blocks the event loop. Here is the official guidelines about it
Related
I am very confused about Javascript referencing. I understand when referencing an object, there is a reference made. Changing the parent changes the copy and vice versa.
What I am confused about is when reassignment changes are made to the parent, the copy retains everything. See my example
let tester = {
"hello": "how are you",
"myArrayHere": [
{ "id": 1, "name": "me" },
{ "id": 2, "name": "you" },
{ "id": 3, "name": "them" },
]
};
var something = tester.myArrayHere.find(x => x.name === "you");
console.log(something);
console.log("--------");
something.id = 99;
console.log(something);
console.log("--------");
console.log(tester.myArrayHere[1]);
console.log("--------");
tester.myArrayHere[1].id = 88;
console.log(something);
console.log("--------");
tester.myArrayHere[1] = {};
console.log(tester.myArrayHere[1]);
console.log("--------");
console.log(something)
If you run that example, something on the last line, still has the entire object, even though two lines above, its reference was re-assigned.
There are other examples of this, such as when you delete things off the parent, etc. If this is purely a reference and not a copy (such as with primitive types) why are these changes not affecting it as it should?
The something variable simply refers to a (pre-defined) object stored in the memory. It's value is the returned value (a referral to an object) of the Array#find method. Variables do not observe any specific path (e.g. tester.myArrayHere[1]), they are not observers. In other words, in this case, JavaScript interpreter doesn't care/remember how you get the object/value before the assignment.
> var a, b; a = b = {};
> a === b
true
> a = {}
> a === b
false
After executing tester.myArrayHere[1] = {};, the second element of the array refers to a new object. The something variable still refers to the old object.
Please check the explanation in a snippet
let tester = { // tester is a link to a object (not an object itself) in memory
"hello": "how are you",
"myArrayHere": [ // tester.myArrayHere is a link to a object (not an object itself) in memory
{ "id": 1, "name": "me" },
{ "id": 2, "name": "you" }, // tester.myArrayHere[1] is a link to a object (not an object itself) in memory
{ "id": 3, "name": "them" },
]
};
var something = tester.myArrayHere.find(x => x.name === "you"); // something now is the same link to the object { "id": 2, "name": "you" }, but not an object itself tester.myArrayHere[1] will be === something link is eqaul to link
console.log(something);
console.log("--------");
something.id = 99;
console.log(something);
console.log("--------");
console.log(tester.myArrayHere[1]);
console.log("--------");
tester.myArrayHere[1].id = 88;
console.log(something);
console.log("--------");
tester.myArrayHere[1] = {}; // now tester.myArrayHere[1] becomes a link to a new object, { "id": 2, "name": "you" } is still in a memory and something is link to it
console.log(tester.myArrayHere[1]);
console.log("--------");
console.log(something)
Its best to conceptualize the code you posted as "having a direct shortcut to the nested field"
const joe = { // (A) joe is an object
id: 42,
name: 'joe',
hobbies: [ // (B) hobbies is an Array, a reference type
'surfing',
'videogames'
]
}
// (C) this variable has no idea that it is part of person object from (A)
const cloneHobbies = joe.hobbies
cloneHobbies.push('boxing')
console.log('***JOE***', joe) // Joe now has a new hobby: boxing
// It also works the other way; Joe's hobbies become someone else's
joe.hobbies.push('karate')
console.log("***CLONE'S HOBBIES***", cloneHobbies) // now has 'karate'
Hope this helps you conceptualize the code.
Cheers,
I currently have an object:
var obj = {
username: "James",
surname: "Brandon",
id: "[2]"
}
and I want to append it to "users.json":
[
{
"username": "Andy",
"surname": "Thompson",
"id": [0],
},
{
"username": "Moe",
"surname": "Brown",
"id": [1]
}
]
Do you know how I might be able to do this?
Thanks in advance.
This answer is assuming that you are working under Node.js.
As I understand your problem you need to solve a few different programming questions.
read and write a .json file
const fs = require("fs");
let usersjson = fs.readFileSync("users.json","utf-8");
transform a json string into a javascript array
let users = JSON.parse(usersjson);
append an object to an array
users.push(obj);
transform back the array into a json string
usersjson = JSON.stringify(users);
save the json file
fs.writeFileSync("users.json",usersjson,"utf-8");
If your code is running in the browser and users.json is an output file, I guess you already have access to its content.
Use the push() method.
Also, note the missing commas in your objects.
var obj = {
username: "James",
surname: "Brandon",
id: "[2]"
};
var users = [
{
"username": "Andy",
"surname": "Thompson",
"id": [0]
},
{
"username": "Moe",
"surname": "Brown",
"id": [1]
}
];
users.push(obj);
console.log( JSON.stringify(users) );
Now that you have the updated array of objects you can upload it to the server (check this question) or offer a download to the user (check this other question).
As you have been already told, there is no way to directly update client-side a file in the server. It is also not possible to save it directly into the client filesystem.
I am trying to get the same results as pythons json.dumps() with sort by keys enabled. This is preformed as a pre-request script for Postman to generate the request hash. The output needs to be sorted valid json which is used as input for hashing. I am new to javascript and see many old answers claiming that objects in javascript cannot be sorted. However there must be a solution to generate the hash given the criteria.
The object structure cannot be changed.
It only needs to support Chrome.
I can use libraries.
requestParams can contain nested objects and arrays which need to be sorted at any depth.
This is my current code. In the Chrome console the object preview for sortedResult is unsorted, however when I expand the object and sub-objects the Chrome console shows sortedResult as sorted, exactly the way it should be. This gives me the impression the sortObject is working. However requestOrdered returns the valid json object but it is not sorted. My initial thoughts are that maybe JSON.stringify() is unsorting it.
const requestRebuilt = {"username": user, "password": password, "sTime": time, "function": function,
"functionParams": requestParams, "salt": salt};
function sortObject(object){
var keys = _.keys(object);
var sortedKeys = _.sortBy(keys, function(key){
//console.log(key);
return key;
});
var sortedObj = {};
var sortedObjJson = "";
for(var index in keys){
var key = keys[index];
//console.log(key + ' ' + typeof object[key]);
if(typeof object[key] == 'object' && !(object[key] instanceof Array)){
sortedObj[key] = sortObject(object[key]);
} else if(object[key] instanceof Array) {
//sortedObj[key] = object[key].sort();
var arrayLength = object[key].length;
for (var i = 0; i < arrayLength; i++) {
sortedObj[key] = sortObject(object[key][i]);
//console.log(object[key][i]);
}
} else {
sortedObj[key] = object[key];
}
}
return sortedObj;
}
const sortedResult = sortObject(requestRebuilt);
console.log(sortedResult);
const requestOrdered = JSON.stringify(sortedResult);
console.log(requestOrdered);
var hash = CryptoJS.SHA256(requestOrdered).toString();
postman.setGlobalVariable("hash", hash);
Example input:
{
"username": "jdoe#mail.com",
"sTime": "2016-03-04T13:53:37Z",
"function": "begin",
"functionParams": {
"tip": "ABC123FFG",
"pad": 4 ,
"passenger": [{
"firstName": "John",
"phone": 1234567890,
"email": "jdoe#mail.com",
"dateOfBirth": "1915-10-02T00:00:00Z",
"bans": {
"weight": 9,
"count": 2
}
}
]},
"salt": "00d878f5e203",
"pep": "sdeODQ0T"
}
In python this is done by the following:
ordered = json.dumps(
{"username": user, "password": password, "time": time, "function": function, "functionParams": functionParams, "salt": salt}
sort_keys=True, separators=(',', ':'))
Result of ordered:
{"function":"begin","functionParams":{"passenger":[{"bans":{"count":2,"weight":9},"dateOfBirth":"1915-10-02T00:00:00Z","email":"jdoe#mail.com","firstName":"John","phone":1234567890}],"pad":4,"tip":"ABC123FFG"},"pep":"sdeODQ0T","salt":"00d878f5e203","sTime":"2016-03-04T13:53:37Z","username":"jdoe#mail.com"}
Pretty printed for easier reading but actual result should not have spaces or new lines:
{
"function": "begin",
"functionParams": {
"passenger": [
{
"bans": {
"count": 2,
"weight": 9
},
"dateOfBirth": "1915-10-02T00:00:00Z",
"email": "jdoe#mail.com",
"firstName": "John",
"phone": 1234567890
}
],
"pad": 4,
"tip": "ABC123FFG"
},
"pep": "sdeODQ0T",
"salt": "00d878f5e203",
"sTime": "2016-03-04T13:53:37Z",
"username": "jdoe#mail.com"
}
It's a common misconception that "object keys are not ordered" in javascript. MDN states that
Although ECMAScript makes iteration order of objects implementation-dependent, it may appear that all major browsers support an iteration order based on the earliest added property coming first (at least for properties not on the prototype).
and ES2015 makes this behaviour standard:
For each own property key P of O that is a String but is not an integer index, in property creation order...
That is, you can rely on the fact that object properties are always iterated in the insertion order (unless you're using delete, see here for details).
So, to sort keys in some object just create a new object and add keys to it in the sorted order:
function sortKeys(x) {
if (typeof x !== 'object' || !x)
return x;
if (Array.isArray(x))
return x.map(sortKeys);
return Object.keys(x).sort().reduce((o, k) => ({...o, [k]: sortKeys(x[k])}), {});
}
////
obj = {
"username": "jdoe#mail.com",
"sTime": "2016-03-04T13:53:37Z",
"function": "begin",
"functionParams": {
"tip": "ABC123FFG",
"pad": 4,
"passenger": [{
"firstName": "John",
"phone": 1234567890,
"email": "jdoe#mail.com",
"dateOfBirth": "1915-10-02T00:00:00Z",
"bans": {
"weight": 9,
"count": 2
}
}
]
},
"salt": "00d878f5e203",
"pep": "sdeODQ0T"
}
sorted = sortKeys(obj);
console.log(sorted);
I am wondering how would I get the next JSON item if I have the key in JavaScript. For example, if I provide the key 'Josh' how would I get the contents of 'Annie' along with the key 'Annie'? Would I have to process the JSON in an array and extract from there?
In addition, I believe that there is a proper term for transforming data from one type to another. Any chance anyone knows what it is... it is just on the tip of my tongue!
{
"friends": {
"Charlie": {
"gender": "female",
"age": "28"
},
"Josh": {
"gender": "male",
"age": "22"
},
"Annie": {
"gender": "female",
"age": "24"
}
}
}
In JavaScript the order of Object properties is not guaranteed (ECMAScript Third Edition (pdf):)
4.3.3 Object An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive
value, object, or function. A function stored in a property of an
object is called a method.
If the order doesn't have to be guaranteed you could do the following:
var t = {
"friends": {
"Charlie": {
"gender": "female",
"age": "28"
},
"Josh": {
"gender": "male",
"age": "22"
},
"Annie": {
"gender": "female",
"age": "24"
}
}
};
// Get all the keys in the object
var keys = Object.keys(t.friends);
// Get the index of the key Josh
var index = keys.indexOf("Josh");
// Get the details of the next person
var nextPersonName = keys[index+1];
var nextPerson = t.friends[nextPersonName];
If order matters I would recommend having another array of to hold the order of the names ["Charlie", "Josh", "Annie"] instead of using Object.keys().
var t = ...;
// Hard code value of keys to make sure the order says the same
var keys = ["Charlie", "Josh", "Annie"];
// Get the index of the key Josh
var index = keys.indexOf("Josh");
// Get the details of the next person
var nextPersonName = keys[index+1];
var nextPerson = t.friends[nextPersonName];
I am used to making JavaScript objects like below but what would be the fastest way of doing the same with C#? It is just static data that will be serialized for a post that will not change. In the past with C# I would create classes and lists for everything but in this scenario I just need to send some practice data. Any help would be appreciated
var data = {
"dealer": {
"keyId": "vfase32sd",
"name": "mark"
},
"seller": [
{
"email": "email#email.com",
"active":false
}
],
"state": [
"TN",
"TX"
]};
See Object and Collection Initializers
Object initializer with Anonymous types
Although object initializers can be used in any context, they are especially useful in
LINQ query expressions. Query expressions make frequent use of
anonymous types, which can only be initialized by using an object
initializer, as shown in the following declaration.
var pet = new { Age = 10, Name = "Fluffy" };
For example:
var data = new
{
dealer = new
{
keyId = "vfase32sd",
name = "mark"
},
seller = new[] {
new {
email= "email#email.com",
active= false
}
},
state = new[]{
"TN",
"TX"
}
};
The rule for converting that js object to c# object is simple:
"x": will be x=
{} will be new {}
[] will be new []{}
Values remain untouched
You can use dynamic typing feature of C#
var data = new
{
dealer = new
{
keyId = "vfase32sd",
name = "mark",
},
seller = new[]
{
new
{
email = "email#email.com",
active = false
}
},
state = new []
{
"TN",
"TX"
}
};