I have a source.json file, and then another file of changes which I parse, and need to write out the array back to the source file. Here is the source.json structure:
{
"users" : [
{
"id" : "1",
"name" : "Albin Jaye"
}
],
"playlists" : [
{
"id" : "1",
"owner_id" : "2",
"song_ids" : [
"8",
"32"
]
}
],
"songs": [
{
"id" : "1",
"artist": "Camila Cabello",
"title": "Never Be the Same"
}
]
}
Here is the changes.json structure. I have a CRUD like "add" directive and the "payload" which is the entry to be added to the source file:
{
"users": [{
"action": "add",
"payload": [{
"id": "1",
"name": "Albin Jaye"
},
{
"id": "2",
"name": "Dave Mustaine"
}
]
}],
"playlist": [{
"action": "add",
"payload": [{
"id": "1",
"owner_id": "2",
"song_ids": [
"8",
"32"
]
}]
}],
"songs": [{
"action": "add",
"payload": [{
"id": "1",
"artist": "Camila Cabello",
"title": "Never Be the Same"
}]
}]
}
I have written this code in Node to read in the source file and parse and then read in the changes file. Once the changes file is read, I iterate over and see what "CRUD" directive is there. When I find "add", I then push the payload to an array. After I get finished, when I console log out I get this:
[
[ 'users', '[object Object],[object Object]' ],
[ 'playlists', '[object Object]' ],
[ 'songs', '[object Object]' ]
]
This is swell. BUT, I need now to take this result and Stringify it I think and place it back into the source JSON file in the appropriate place. I think it will be some type of ```fs.appendFile()`` function, but not sure. Here is my code so far:
// Stream in source file
fs.readFile('./' + inputFile, (err, data) => {
if (err) throw err;
let input = JSON.parse(data);
//console.log(input);
});
// Stream in changes file
fs.readFile('./' + changesFile, 'utf-8', (err, jsonString) => {
if (err) {
console.log(err);
} else {
try {
const data = JSON.parse(jsonString);
const array = [];
Object.entries(data).map(([key, [{ action, payload }]]) => {
switch (key) {
case 'users': {
if (action === 'add') {
console.log("it's an add");
array.push([`${key}`, `${payload}`]);
}
break;
}
case 'playlists': {
if (action === 'add') {
console.log("it's an add");
array.push([`${key}`, `${payload}`]);
}
break;
}
case 'songs': {
if (action === 'add') {
console.log("it's an add");
array.push([`${key}`, `${payload}`]);
}
break;
}
}
});
console.log(array);
} catch (err) {
console.log('Error parsing JSON', err);
}
}
});
// after we have merged changes and source we need to write out
// Don't know how to merge the changes array I created above back into the
// source.json file?
fs.appendFile('./' + outputFile, JSON.stringify(array, null, 2), err => {
if (err) {
console.log(err);
} else {
console.log('File sucessfully written');
}
});
I'm not 100% sure where you're going with your implementation, but I think a better way to go about this would be to update the original data object with the changes, and then write the changes out to a file:
const data = JSON.parse(fs.readFileSync(inputFile));
const changes = JSON.parse(fs.readFileSync(changesFile));
for(const [table, actions] of Object.entries(changes)){
if(!(table in data))continue;
for(const actionDetail of actions){
if(actionDetail.action === 'add'){
data[table].push(...actionDetail.payload);
}
}
}
fs.writeFileSync(inputFile, JSON.stringify(data));
Related
I'm using knex and this is my code to get a question_info object after requesting from the database.
let question_info = knex.select('id', 'question', 'pre_question', 'response_options_id').from('_questions').whereNot({'progress': 100}).andWhere({'school_id': school_id}).orderBy('date_created', 'asc')
.map((question) => {
//going through all possible questions
helpers.debug("Question", question);
//checking if there are responses
return knex('_responses').where({'question_id': question.id, 'student_id': event.user.id}).first()
.then((response) => {
//if no responses, return question_info object
if(!response){
return knex.select('options_json').from('_response_options').where({'id': question.response_options_id}).first()
.then((options) => {
return {
question_id: question.id,
question: question.question,
pre_question: question.pre_question,
response_options: JSON.parse(options.options_json)
}
});
}
});
}).catch((e) => {
console.error('Error', e);
});
When the first question has already been answered, my return value shows up as null. How do I just have the returned array without null using knex correctly.
[
null,
{
"question_id": "2",
"question": "What is second question?",
"pre_question": "Hey!!",
"response_options": [
{
"title": "Yes",
"value": "Yes"
},
{
"title": "Maybe",
"value": "Maybe"
},
{
"title": "No",
"value": "No"
}
]
},
{
"question_id": "3",
"question": "Third Question?",
"pre_question": "Yo",
"response_options": [
{
"title": "Yes",
"value": "Yes"
},
{
"title": "Maybe",
"value": "Maybe"
},
{
"title": "No",
"value": "No"
}
]
}
]
Looks like your problem is that if second query indeed did return response you are not returning anything.
// this part of your code seems to be wrong
if (!response) {
// return choices because there was no response found
return knex('choices')...
}
It should be something like this:
// this part of your code seems to be wrong
if (!response) {
// return choices because there was no response found
return knex('choices')...
} else {
return response;
}
EDIT this would be more or less how to do the same stuff with single query and joins (I didn't test this so it probably doesn't work, but it should give general idea how to achieve it):
let question_info = knex('_questions as q')
.select(
'id',
'question',
'pre_question',
'response_options_id',
'r.id as r_id',
'options_json'
)
.join('_responses as r', builder => {
builder
.on('q.id', 'r.question_id')
.onVal('r.student_id', event.user.id);
})
.join('_response_options as ro', 'q.response_options_id', 'ro.id')
.where('q.school_id', school_id)
.whereNot('q.progress', 100)
.whereNull('r.id')
.orderBy('q.date_created', 'asc')
.map(notAnsweredQuestion => {
return {
question_id: notAnsweredQuestion.id,
question: notAnsweredQuestion.question,
pre_question: notAnsweredQuestion.pre_question,
response_options: JSON.parse(notAnsweredQuestion.options_json)
};
}).catch((e) => {
console.error('Error', e);
});
So Basically, i have a JSON file which consists of user and group data. I want to delete a particular group. This is what my JSON file looks like:
authdata.json:
[{
"name": "Allan",
"role": ["Group Admin", "Super Admin"],
"group": ["Cool-Group", "ss"]
}, {
"name": "super",
"group": ["Nerd Group"],
"role": ["Super Admin"]
}, {
"name": "Terry",
"role": ["Group Admin"],
"group": ["Cool-Group"]
}, {
"name": "Kaitlyn",
"role": ["Group Admin"],
"group": ["Nerd-Group"]
}, {
"name": "Alex",
"role": ["Group Admin"],
"group": ["Cool-Group"]
}]
I'm just confused on how to handle a http delete request in nodeJS?
this how my angular component is sending the request to the server:
remove.component.ts:
RemoveGroup() {
this.httpService.delete < any > (this.apiURL + 'deletegroup', {
group: this.groups
}).subscribe(
data => {
if (data['success'] == true) {
alert(data.group + " is removed");
} else {
alert("No groups found");
}
},
(err: HttpErrorResponse) => {
console.log(err.message);
}
);
}
This is the server side on NodeJS (reading the json file, assigning the data to a variable, trying to delete the group (which is not working for me) and writting back to the JSON file):
deletegroup.js:
app.delete('/api/deletegroup', (req, res) => {
// localhost:3000/api/auth?username=Terry
var groups = req.body.group;
var userObj;
fs.readFile('authdata.json', 'utf8', function(err, data) {
if (err) {
console.log(err);
//Some error happended opening the file. No Success
res.send({
'group': '',
'success': false
});
} else {
userObj = JSON.parse(data);
for (let i = 0; i < userObj.length; i++) {
for (let j = 0; i < userObj.length; j++) {
if (userObj[i].group[j] == groups) {
userObj.splice(userObj.indexOf(groups), 1);
//find first instance of user name and success
}
}
}
var newdata = JSON.stringify(userObj);
fs.writeFile('authdata.json', newdata, 'utf-8', function(err) {
if (err) throw err;
//Send response that registration was successfull.
res.send({
'group': groups,
'success': true
});
});
//no username was found that matched
}
});
});
I assume that the problem is not with HTTP DELETE request. The concern is with how to remove a child node. See the below code snippet. You can pass the groups as an array to the deleteGroup function and see the result.
var data = [{
"name": "Allan",
"role": ["Group Admin", "Super Admin"],
"group": ["Cool-Group", "ss"]
}, {
"name": "Terry",
"role": ["Group Admin"],
"group": ["Cool-Group"]
}];
function deleteGroup(groupArray) {
groupArray.map((needle)=>{
data.map((userObj) => {
if(userObj.group) {
userObj.group.map((groupName, index)=>{
if (groupName == needle){
userObj.group.splice(index)
}
});
} else {
console.log("No group present for this user.")
}
});
});
return data
}
//uncomment below line & run in console to see the results
//console.log(deleteGroup(["Cool-Group"]))
Try out directly in Jsbin - https://jsbin.com/pejidag/1/edit?js,console
Happy coding!
How can I get the names of different activity in an array by using map function in this type of response. So that in a new array, assume that activity[] i will get names of all the activities mention below.
if the array is
const response = [
{
"Groups" : {
"Roles" : {
"submission": {
"subject" : {
"name": "history",
}
}
}
}
}
];
I managed to do this using an IIFE but there may be cleaner ways
assuming there in one object in the array and no other path to other permission
const response = [
{
"Roles" : {
"Permission" : {
"PERMISSION1": {
"Activity" : {
"name": "Manage Clients",
}
},
"PERMISSION2": {
"Activity" : {
"name": "Manage Users",
}
}
}
}
}
];
let activities = (() => {
let res = []
for (let perm in response[0].Roles.Permission) {
for (let act in response[0].Roles.Permission[perm]) {
res.push(response[0].Roles.Permission[perm][act].name)
}
}
return res})()
console.log(activities)
At first, you should convert Permission object to array, cause object doesn't have method map.
Then you could use map function where you can collect all your permissions' names for every item in response
const response = [{
"Roles": {
"Permission": {
"PERMISSION1": {
"Activity": {
"name": "Manage Clients",
}
},
"PERMISSION2": {
"Activity": {
"name": "Manage Users",
}
}
}
}
}];
response.forEach((item) => {
item.Activities = Object.values(item.Roles.Permission).map((permission) => permission.Activity.name)
});
alert(JSON.stringify(response));
The only array you have is response. If each item in response has a Roles that has a Permission that has several keys with objects that have Activity with name then you can do the following:
var response = [
{
Roles: {
Permission: {
PERMISSION1: {
Activity: {
name: 'Manage Clients',
},
},
PERMISSION2: {
Activity: {
name: 'Manage Users',
},
},
},
},
},
];
console.log(
response.map(
(item) =>
Object.values(item.Roles.Permission)
.map(
(permission) => permission.Activity.name
)
)
);
I recommend using a flatMap, so use .reduce.
const response = [{
"Roles": {
"Permission": {
"PERMISSION1": {
"Activity": {
"name": "Manage Clients",
}
},
"PERMISSION2": {
"Activity": {
"name": "Manage Users",
}
}
}
}
}];
const activityNames = response.reduce(function (acc, res) {
const permissions = res.Roles.Permission;
const permissionKeys = Object.keys(permissions);
const names = permissionKeys.map(function(permissionKey) {
return permissions[permissionKey].Activity.name;
});
acc.push(...names);
return acc;
}, [])
console.log(activityNames); // ["Manage Clients", "Manage Users"]
I am trying to parse the below JSON (format), flatten it out and get the required key / value (output) as mentioned below. I am trying to figure out the best way to do that.
I need to get the displayName and its corresponding parent
For Ex:
Features: taxoFeatures
Input:
{
"Shop": {
"subFilter": [
{
"taxoFeatures": {
"displayName": "Features"
}
},
{
"color_base": {
"displayName": "Colour"
}
}
],
"displayName": "Shopping"
},
"Support": {
"subFilter": [
{
"contentType": {
"displayName": "Content"
}
}
],
"displayName": "Support documents"
}
}
Expected output:
{
"Shopping": "Shop",
"Features":"taxoFeatures",
"Colour":"color_base",
"Content":"contentType",
"Support documents":"Support"
}
I was thinking of looping through the JSON and find the key and add the corresponding key and displayName value (and then loop through each child array and store those values as well). Any other ideas?
let customName = {};
for (const key in filter) {
if (filter.hasOwnProperty(key)) {
const value = filter[key];
if (isNotEmpty(value.displayName)) {
customName[value.displayName] = key;
}
}
}
You could use this recursive function which builds up the result object using reduce and Object.assign:
function transform(filter) {
return Object.keys(filter).reduce( (acc, key) => {
const value = filter[key];
if (Object(value) === value) { // it's an object
if ('displayName' in value) {
acc[value.displayName] = key;
}
Object.assign(acc, transform(value)); // recurse
}
return acc;
}, {});
}
// Sample input
const filter = {
"Shop": {
"subFilter": [
{
"taxoFeatures": {
"displayName": "Features"
}
},
{
"color_base": {
"displayName": "Colour"
}
}
],
"displayName": "Shopping"
},
"Support": {
"subFilter": [
{
"contentType": {
"displayName": "Content"
}
}
],
"displayName": "Support documents"
}
}
console.log(transform(filter));
Well, basically yes. You need to loop it throught, but as u have same code you can make a recursion, something like this:
function doit (json) {
let ret = {};
Object.keys(json).forEach((key) => {
const childs = (!!json[key].subFilter) ? json[key].subFilter.map(doit) : [];
ret = Object.assign(ret, { [json[key].displayName]: key }, ...childs);
});
return ret;
}
Here is another way to do it:
var data = {
"Shop": {
"subFilter": [
{
"taxoFeatures": {
"displayName": "Features"
}
},
{
"color_base": {
"displayName": "Colour"
}
}
],
"displayName": "Shopping"
},
"Support": {
"subFilter": [
{
"contentType": {
"displayName": "Content"
}
}
],
"displayName": "Support documents"
}
};
var res = {};
function search(obj){
for(item in obj) {
res[obj[item].displayName] = item;
if (obj[item].subFilter)
obj[item].subFilter.forEach((subitem)=>search(subitem));
}
}
search(data);
console.log(res);
JSFiddle: https://jsfiddle.net/7b6bcjfk/
I have the following database structure stored in the mongoDB:
"location" : "Halifax",
"students" : [
{
"name": "Mike",
"ID": "B00123456",
"images": [
{
"image_url":"",
"image_id":""
}
]
},
{
"name": "Rinan",
"ID": "B00999999",
"images": [
{
"image_url":"",
"image_id":""
}
]
}
]
My question is: how do I push a new object to images array inside a student named Mike who has an ID of "B00123456", I know I should use mongoDB's update and set method. But I just couldn't find a way to achieve that. The result I want is:
"location" : "Halifax",
"students" : [
{
"name": "Mike",
"ID": "B00123456",
"images": [
{
"image_url":"",
"image_id":""
},
{
"image_url":"www.example.com",
"image_id":"uqxhqbxqx_1219"
}
]
},
{
"name": "Rinan",
"ID": "B00999999",
"images": [
{
"image_url":"",
"image_id":""
}
]
}
]
Below is what I am trying using MongoDB's update and set:
// Connect and create a new doc
MongoClient.connect('mongodb://username:password#iad1- mongos0.objectrocket.com:someNode/db_name', functionb(err, db) {
if (err) {
console.dir(err);
console.log("error connected to mongodb");
} else {
var collection = db.collection('student_info_collection');
var student_name = req.body.name;
var student_id = req.body.ID;
collection.update(
{ location:"Halifax" },
{ ID:student_id}
{ name: student_name},
{$push: {
{
"images": [
{
"image_url":"www.example.com",
"image_id":"uqxhqbxqx_1219"
}
]
}
}
}, function(err,result){
if (err)
console.log("Something's wrong");
else
res.sendStatus(200);
}
);
}
});
Any help?
The update() function is
update(selector, document[, options][, callback])
The first parameter is selector, please try this one
var student_name = req.body.name;
var student_id = req.body.ID;
collection.update(
{ location:"Halifax",
'students.ID': student_id,
'students.name': student_name},
{$push: { "students.$.images":
{
"image_url":"www.example.com",
"image_id":"uqxhqbxqx_1219"
}
}
}, function(err,result){