I am knew to JSON, this is the first time i have worked with it.
I have created a script using node to pull music(Artists and Song Titles) from a radio stations website as the radio station plays them. Currently i am putting them into a JSON file by appending them to the end of the file.
I would like to fill them into an array each time a new song is found instead. How do i go about doing this?
Here is my current code
var fs = require('fs');
var request = require('request');
var cheerio = require('cheerio');
var schedule = require('node-schedule');
var rule = new schedule.RecurrenceRule();
//Timer to run every 3 minutes (average song time)
rule.minute = new schedule.Range(0, 59, 3);
var j = schedule.scheduleJob(rule, function(){
console.log('LOADING.......................');
//URL for site you want to get the Songs from
url = '#';
request(url, function(error, response, html){
if(!error){
var $ = cheerio.load(html);
var artist, stitle;
var songs = {artist : "", stitle : ""};
//ID for artist
$('#').each(function(){
var data = $(this);
artist = data.text();
songs.artist = artist;
})
//ID for song title
$('#').each(function(){
var data = $(this);
stitle = data.text();
songs.stitle = stitle;
})
}
//Reading current list of songs
var content;
content = fs.readFileSync('output.json', "utf8");
//Searching current list for song it wants to add
var text = content;
var search = text.search(stitle);
//Only adding song if it cant find new song in list
if(search >= 0) {
console.log('Song already exists');
} else {
fs.appendFile('output.json', JSON.stringify(songs, null, 4) + ",\n", function (err) {
console.log('Song successfully added!');
});
}
})
});
Currently my JSON output looks like:
{
"artist": "FOO FIGHTERS",
"stitle": "BEST OF YOU"
},
{
"artist": "GAY NINETIES",
"stitle": "LETTERMAN"
},
{
"artist": "VANCE JOY",
"stitle": "RIPTIDE"
},
{
"artist": "NIRVANA",
"stitle": "IN BLOOM"
}
I would like to fill an array of songs like this:
{
songs : [
{
"artist": "FOO FIGHTERS",
"stitle": "BEST OF YOU"
},
{
"artist": "GAY NINETIES",
"stitle": "LETTERMAN"
},
{
"artist": "VANCE JOY",
"stitle": "RIPTIDE"
},
{
"artist": "NIRVANA",
"stitle": "IN BLOOM"
}
]
}
I know that i need to use something alongs the lines:
var songs = [];
for (var song in songs) {
songs.push({artist : "", stitle : ""});
}
But i don't know how to incorporate into my code, any help would be lovely, thanks guys
Okay, so if I understand your question correctly, you would like to load the JSON data; then append a song into the array; and then convert it back into JSON?
// load new song data first:
var newSong = {
artist: "FOO BAR FIGHTERS",
stitle: "IF THEN ELSE"
}
// then load data:
var jsonString = "[{:[,],:}]" // load JSON file here!
var data = JSON.parse(jsonString) // turn JSON string into an actual object
/*
at this point, you have access to data.song,
which is the array of all songs in the list.
*/
// now check if it's already in the list:
var alreadyInList = false
for(var i = 0; i < data.song.length; i ++)
{
if(data.song[i].stitle === newSong.stitle) alreadyInList = true
}
// if not, push it:
if(!alreadyInList) data.song.push(newSong)
// then stringify the object again for storage:
var backToString = JSON.stringify(data)
console.log(data) // output back to file
Is this what you're looking for?
Related
I have a JSON file that I need to add Comments into and then update the file.
I've created an array for the new comments
//ADDING NEW COMMENTS
//add new comment within project
$scope.updatecomments = [];
$scope.addnewcomment = function() {
$scope.updatecomments.push({
"Author": "test",
"Text": $scope.NewComment
})
}
I can post the new comments into the JSON file but it overrides the past comments.
I have tried to merge the older comments with the new comments with the following
$scope.updatecomments = [];
$scope.addnewcomment = function() {
$scope.updatecomments.push({"Author": "test" ,"Text": $scope.NewComment}).concat($scope.Comments, $scope.updatecomments);
}
$scope.updatecomments = [].concat($scope.updatecomments,
$scope.projectDetails.Comments);
$scope.addnewcomment = function() {
$scope.updatecomments.push({
"Author": "test",
"Text": $scope.NewComment
});
}
I also tried making a new function that when called combines the two and then post the combined array
$scope.combine = [];
$scope.combineComments = function (){
var jsonStr = $scope.projectDetails.Comments;
var obj = JSON.parse(jsonStr);
obj['Comments'].push({"Author":"Test","Text":$scope.NewComment});
jsonStr = JSON.stringify(obj);
}
}
I have been going over this for the past few days now and can't seem to get it. Any help would be greatly appreciated!
EDIT
Sample Data of already existing data in JSON file
{
"Comments":[{
"Author": "John Doe",
"Text": "Work completed"
}]
}
Want to add to this (is from html input text tag) stored as NewComment
{
"Comments":[{
"Author": "Test",
"Text": "Project flagged"
}]
}
Edit 2
This is how I'm getting my projects data
/FIND PROJECTS - ADD TO LIST
$scope.projectList = [];
for (var id = 0; id < 30; id++) {
var targetURL = 'https://happybuildings.sim.vuw.ac.nz/api/sooleandr/project.'+id+'.json';
$http.get(targetURL).then(
function successCall(response){
$scope.projectList.push(response.data);
}
);
}
I then use this to access the selected information
//script
$scope.showData = function(x){
$scope.projectDetails = x;
};
//html
<ul class = 'pList'>
<li ng-repeat = 'x in projectList' class = 'pbList'>
<button class = 'pbutton' ng-click = 'showData(x)'>
<label ng-model ='pID'>Project ID: </label>{{x.ProjectID}} <br>
<label id ='pName'>Project Name: </label> {{x.Name}} <br>
<label id ='bID'>Building ID: </label>{{x.BuildingID}}<br>
<label id ='sDate'>Start Date: </label>{{x.StartDate}}
</button>
</li>
</ul>
Then I have the following variables to post
$scope.updateProject = function (projectDetails){
var updateproject = {
"ProjectID":$scope.projectDetails.ProjectID,
"Name":$scope.projectDetails.Name,
"BuildingID":$scope.projectDetails.BuildingID,
"StartDate":$scope.projectDetails.StartDate,
"EndDate":$scope.projectDetails.EndDate,
"Status":$scope.projectDetails.Status,
"ContactPerson":$scope.projectDetails.ContactPerson,
"Contractor":$scope.projectDetails.Contractor,
"ProjectManager":$scope.projectDetails.ProjectManager,
"Works": $scope.projectDetails.works,
"Comments":$scope.updatecomments,
};
$http.post("https://happybuildings.sim.vuw.ac.nz/api/sooleandr/update.project.json", updateproject).then(
function success(){
alert("Project Successfully Posted");
},
function error(){
alert("Error: Couldn't post to server");
}
)
};
It posts perfectly fine but it currently overrides the comments. I want to be able to add a new comment and still keep all the past comments. So I want to be able to push/add the comments into the full POST.JSON array.
Hope this makes a bit more sense
OK, updating answer after looking at provided code.
It appears you may be under the impression that $scope.projectDetails.Comments is a JSON string, when, in fact.. it's the actual Comments array.
I would try this for the addnewcomment function:
//ADDING NEW COMMENTS
//add new comment within project
$scope.updatecomments = undefined;
$scope.addnewcomment = function() {
$scope.updatecomments = $scope.updatecomments || $scope.projectDetails.Comments;
$scope.updatecomments.push({
"Author": "test",
"Text": $scope.NewComment
})
}
IF it just so happens to be a JSON string (highly unlikely), then I would update the combine function to this:
$scope.combineComments = function (){
var jsonStr = $scope.projectDetails.Comments;
var obj = JSON.parse(jsonStr);
obj.push({"Author":"Test","Text":$scope.NewComment});
jsonStr = JSON.stringify(obj);
}
}
EDIT
I'm adding another answer from my original because of the possibility things will break when there are no updated comments
//ADDING NEW COMMENTS
//add new comment within project
$scope.addnewcomment = function() {
$scope.projectDetails.Comments.push({
"Author": "test",
"Text": $scope.NewComment
})
}
Then in the POST, change to:
"Comments":$scope.projectDetails.Comments
I have figured out how to combine the two with
$scope.combinecomments = [];
$scope.combine = function (){
$scope.combinecomments.push($scope.projectDetails.Comments);
$scope.combinecomments.push($scope.updatecomments);
}
Except now it doesn't post the combined comments
$scope.ProjectID='$scope.ProjectID';
$scope.Name = '$scope.Name';
$scope.BuildingID = '$scope.BuildingID';
$scope.StartDate = '$scope.StartDate';
$scope.EndDate = '$scope.EndDate';
$scope.Status = '$scope.Status';
$scope.ContactPerson = '$scope.ContactPerson';
$scope.Contractor ='$scope.Contractor';
$scope.ProjectManager = '$scope.ProjectManager';
$scope.Works = '$scope.works';
$scope.Comments ='$scope.comments';
$scope.updateProject = function (projectDetails){
var updateproject = {
"ProjectID":$scope.projectDetails.ProjectID,
"Name":$scope.projectDetails.Name,
"BuildingID":$scope.projectDetails.BuildingID,
"StartDate":$scope.projectDetails.StartDate,
"EndDate":$scope.projectDetails.EndDate,
"Status":$scope.projectDetails.Status,
"ContactPerson":$scope.projectDetails.ContactPerson,
"Contractor":$scope.projectDetails.Contractor,
"ProjectManager":$scope.projectDetails.ProjectManager,
"Works": $scope.projectDetails.works,
"Comments":$scope.combinecomments,
};
$http.post("https://happybuildings.sim.vuw.ac.nz/api/sooleandr/update.project.json", updateproject).then(
function success(){
alert("Project Successfully Posted");
},
function error(){
alert("Error: Couldn't post to server");
}
)
};
It successfully posts the project except for the comments. It doesn't seem to like my combined array. When I post $scope.updatecomments it will post that but not the $scope.combinecomments.
I'll make a new question for this.
I am currently trying to have a script that I can use in order to turn google sheet data into json that can include neighboring AND nested objects. Currently what I have is a script that can allow for the sheet data to turn into json that allows for nesting objects BUT it does not allow the ability to end one object and start a new one so there cannot be any neighboring objects and instead there's one parent object with children objects in it which is not what I'm after. I'm hoping that I'm just missing something in the current script in order to be able to end and start new objects so I will add the script below, thank you for any contributions to this question!
function formJSON() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var data = sheet.getDataRange().getValues();
var currentObject = {};
var output = currentObject;
for (var i = 0; i < data.length; i++) {
if (data[i][1]) {
currentObject[data[i][0]] = data[i][1];
}
else {
var newObject = {};
currentObject[data[i][0]] = newObject;
currentObject = newObject;
}
}
Logger.log(JSON.stringify(output));
}
EDIT: Here I will provide the current result vs the result I'm after. The first result is from the sheet I have added as an image.
Current Result:
{
"": {
"asset": {
"button": {
"viewPDF": "View PDF",
"viewSurvey": "View Survey",
"viewPPT": "View PPT",
"viewLink": "View Link",
"rejoinMeeting": "Rejoing Meeting",
"labels": {
"associatedWith": "Associated Content",
"attendees": "Attendees in this session",
"filesAndDocs": "Files and Documents",
"location": "Location",
"messages": {
"errorRetrieving": "There was an error retrieving the session details",
"noAttendees": "Nobody is watching this session currently",
"browser": {
"messages": {
"notSupported": "Your browser is not supported",
"update": "Please update"
}
}
}
}
}
}
}
}
Desired Result:
"asset": {
"buttons": {
"viewPDF": "View PDF",
"viewSurvey": "View Web Page",
"viewPPT": "View Presentation",
"viewLink": "View Link",
"rejoinMeeting": "Rejoin Meeting"
},
"labels": {
"associatedWith": "Associated Content",
"attendees": "Attendees in this Session",
"filesAndDocs": "Files and Documents",
"location": "Location",
"notStarted": "This session hasn't started yet.",
"playlist": "Session Playlist",
"premiumSponsors": "Premium Sponsors",
},
"messages": {
"errorRetrieving": "There was an error retrieving the session details.",
"noAttendees": "Nobody is watching this session currently",
"pointsForDocument": "viewing a document",
"pointsForRatingAsset": "rating this asset",
"pointsForVideo": "watching a video",
"problemSaving": "There was a problem saving your rating. Please try again."
}
},
"browser": {
"messages": {
"notSupported": "Your Browser Is Not Supported",
"update": "Please download the most up-to date version of one of the following and try again"
}
},
Please have a look at the following:
var data = sheet.getDataRange().getValues();
var currentObject = {};
var title ='';
var newObject = {};
for (var i = 0; i < data.length; i++) {
if (data[i][1]) {
newObject[data[i][0]] = data[i][1];
}
else {
if(data[i][0] !="" && data[i][0] !=" "){
if(title != ""){
currentObject[title] = newObject;
}
title = data[i][0];
newObject = {};
}
}
}
Logger.log(JSON.stringify(currentObject));
While this is not a full solution, I think that it should point you into the right direciton.
The idea is that you should have some variable (in this case title) which is defined / overwriten in the else statement and which is the key to which nested objects will be assigned during the next if conditions
Once the else condition is entered again, title is overwritten wiht the next nested object key.
I have this code in my app.js for send chat and read chat in my application
$scope.messageshistory = {};
$scope.tmp = {};
// send message
$scope.sendMessage = function(){
$scope.messages = {
from : $scope.datauser['data']['_id'],
fromname : $scope.datauser['data']['nama'],
to : $scope.tmpuserid,
message : $scope.tmp['sendmessage'],
time : moment()
};
//event emit message
socket.emit('message',$scope.messages,function(callback){
if(!callback['error']){
$scope.messages['time'] = moment($scope.messages['time']).format('DD-MMMM-YYYY HH:MM');
if ($scope.messageshistory.hasOwnProperty($scope.tmpuserid)){ //yg di json yg paling awal
$scope.messageshistory[$scope.tmpuserid].push($scope.messages);
}else{
$scope.messageshistory[$scope.tmpuserid] = [];
$scope.messageshistory[$scope.tmpuserid].push($scope.messages);
}
$scope.tmp['sendmessage'] = '';
}else{
var msg = callback['error'];
navigator.notification.alert(msg,'','Error Report', 'Ok');
}
$scope.$apply();
});
};
//event read message
socket.on('message', function (data) {
window.plugin.notification.local.add({
id : moment(),
title : data['fromname'],
message : data['message'].substr(0,20) + ' ...',
led : 'A0FF05',
json : JSON.stringify({ routes:'chat', nama :data['fromname'],from:data['from'] })
});
data['time'] = moment(data['time']).format('DD-MMMM-YYYY HH:MM');
if ($scope.messageshistory.hasOwnProperty(data['from'])){
$scope.messageshistory[data['from']].push(data);
}else{
$scope.messageshistory[data['from']] = [];
$scope.messageshistory[data['from']].push(data);
}
for(var i = 0; i<= $scope.datauser['data']['friends'].length; i++){
if($scope.datauser['data']['friends'][i]['userid'] == data['from']){
$scope.datauser['data']['friends'][i]['ischat'] = true;
break;
}
};
$scope.$apply();
});
my question is how to take the last value in message property from $scope.messageshistory, because $scope.messages is for sending the message and $scope.messageshistory is to keep the chat history. This is the chat activity image:
just from this activity, $scope.messageshistory will save the data in it JSON as:
{
"5512": [{
"from": "561c",
"fromname": "ryan",
"to": "5512",
"message": "hey",
"time": "18-Maret-2016 21:03"
}, {
"from": "5512",
"fromname": "sasa",
"to": "561c",
"message": "hello",
"time": "18-Maret-2016 21:03",
"_id": "593s"
}]
}
I get this value from using angular.toJson($scope.messageshistory), and this array will always add up if the chat activities still going on. And my intention to get the last value in message property from $scope.messageshistoryis to use in Text-to-Speech feature in my application. This is the code:
$scope.speakText = function() {
TTS.speak({
text: **this the place for the code**,
locale: 'en-GB',
rate: 0.75
}, function () {
// handle the success case
}, function (reason) {
// Handle the error case
});
};
it will read the last message in $scope.messageshistory. So, what code that I must write to take the last value?
You have to do the following:
var msgs = $scope.messageshistory[$scope.tmpuserid]
var yourLastMessage = msgs[msgs.length-1].message
// you could also add a quick check so you don't get
// an error if the messages array is emtpy :
// var yourLastMessage = (msgs && msgs[msgs.length-1] ? msgs[msgs.length-1].message : null)
Edit
Some explanation per your comment :
var msgs = $scope.messageshistory[$scope.tmpuserid]
// msgs is now an Array containing Objects
// [{message : 'xxx'},{message : 'yyy'}]
// we take the last entry of the msgs Array (msgs.length-1)
// so msgs[msgs.length-1] is the last object ({message : 'yyy'})
// and finally we take the 'message' property' of that object:
var yourLastMessage = msgs[msgs.length-1].message
assuming that the keys in the history object are ascending numbers and taking into account that the order of keys in an object is not specified by W3C you will have to do the following:
get all keys
find the "latest" (hence the biggest number)
fetch it
so you could do for example
var keys = Object.keys($scope.messagehistory);
keys.sort (function (a, b) {
return a - b;
});
var result = keys[keys.length - 1];
I wrote code that parsing a lot of words (innerHTML) from some webpages.
and I'd like to insert data to json file directly..
Here is my js code...
var words = [];
var casper = require('casper').create();
function getWords() {
var words = document.querySelectorAll('td.subject a');
return Array.prototype.map.call(words, function(e) {
return e.innerHTML;
});
}
casper.start('http://www.todayhumor.co.kr/board/list.php?table=bestofbest', function() {
words = this.evaluate(getWords);
});
for (var i=2; i <=5; i++) {
casper.thenOpen('http://www.todayhumor.co.kr/board/list.php?table=bestofbest&page='+i, function() {
words = words.concat(this.evaluate(getWords));
});
}
casper.run(function() {
// echo results in some pretty fashion
this.echo(words.length + ' links found:').exit();
this.echo(words.join('\n')).exit();
});
and
I run this code through terminal like this!
username#wow:~/workspace/app/assets/javascripts $ casperjs application.js
and the result is (for example)
150 words found:
apple
banana
melon
kiwi
citrus
watermelon
passionfruit
mango
orange
...
So I want to insert this data in "word" part of my json file (example code of json below)
and make other columns("type": "fruit" and "spell":) automatically added
{ "my_initial_words": [
{
"type": "fruit",
"word": "apple",
"spell": "ap"
},
{
"type": "fruit",
"word": "banana",
"spell": "ba"
},
{
"type": "fruit",
"word": "melon",
"spell": "me"
}
]
}
----------------------------------------------------------------------------
thanks for adding more answer!..
but I couldn't catch where should I put these code
Could you tell me once more that... Which code you gave me executes "Saving the results to JSON file?" because I have to read json file(makeyourap.json) in my seeds.rb file like this
require 'json'
file = File.open(Rails.root.join('db','makeyourap.json'))
contents = file.read
json = ActiveSupport::JSON.decode(contents)["my_initial_words"]
So, something like this?
function makeTypeObject(name, type) {
return {
name: name,
type: type,
spell: name.substr(0,2)
};
}
var wordDesc = words.map(function (word) {
return makeTypeObject(word, "fruit");
});
var finalObject = {
my_initial_words: wordDesc
};
var jsonString = JSON.stringify(finalObject);
// if you want prettyprint, try JSON.stringify(finalObject, null, "\t");
I hope this helps.
Write to file via casper
If you want to have a file from which you read and write, appending content, you can do it like this:
var fs = require('fs');
var FILENAME = 'makeyourap.json';
function add_new_fruits(fruits) {
var data;
if ( fs.isFile(FILENAME) ) {
data = fs.read(FILENAME);
} else {
data = JSON.stringify({'my_initial_words' : [] });
}
var json = JSON.parse(data);
fruits.forEach(function(word) {
json.my_initial_words.push({"type": "fruit",
"name": word,
"spell": word.slice(0,2)});
});
data = JSON.stringify(json, null, '\t');
fs.write(FILENAME, data, "w");
}
Use this instead of the older this.echo. Just call it as
casperjs application.js
This either reads the object from a file, or creates it if it does not exist. Then, it appends each new object from the new fruits (including duplicates), and writes it back to FILENAME.
Previous approach: how to roll your own
create Object
So first, you want to create an object that only has the parameter my_initial_words with values as above.
You can create a function via
function createFinal(wordArray) {
var out = [];
wordArray.forEach(function(word) {
out.push({"type": "fruit", "name": word, "spell": word.slice(0,2)});
});
return out;
}
to create the array. Then, create the object via
var my_object = { "my_initial_words": createFinal(words) };
to JSON
Javascript has a built-in JSON-object. With a javascript-object like
var my_object = { "my_initial_words": ...
as above, use
JSON.stringify(my_object)
to get the JSON representation to write.
Older: write to file via redirection
Before, you had
this.echo(words.join('\n')).exit();
which gave you the basic list. Using this.echo, try replacing this by
var my_object = { "my_initial_words": createFinal(words) };
this.echo(JSON.stringify(my_object)).exit();
This prints to standard output. Just remove the other this.echo line (150 words found) and redirect the output via
casperjs application.js > makeyourap.json
If you want to write to file in casperjs, look at write-results-into-a-file-using-casperjs.
I am writing a script on parse.com's javascript cloud code SDK. Here is the information I have saved in my parse.com account and what I am trying to do with it.
I have a bunch of items saved in a parse class called TestItem, theses items have an objectId, item name, meal time (lunch, dinner) and a location for there columns. I also have a class called UserFavourites. In this class the objects have an objectId, item name and a pointer to the user who saved the item as a favourite.
And with this information I am trying to write a cloud code script in javascript. That will match the an item(s) to the item(s) that a user has favourited and send them a push notification saying where and what the item is and the location of the item. I have some code that will do that but this code will send a different notification for each item which could get annoying for the user here is that code.
Parse.Cloud.define("push", function(request, response) {
var TestItem = Parse.Object.extend("TestItem");
var query = new Parse.Query(TestItem);
query.limit(1000);
query.equalTo('school', 'Union College (NY)');
query.find({
success: function(resultsItem) {
//console.log("Successfully retrieved " + resultsItem.length + " :1111.");
for (var i = 0; i < resultsItem.length; i++) {
var object = resultsItem[i];
var item = object.get('item');
var school = object.get('school');
var meal = object.get('meal');
var meal = meal.toLowerCase();
var diningLocation = object.get('schoolMenu');
//var itemArray = [];
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryFavourite = new Parse.Query(UserFavourite);
queryFavourite.limit(1000);
queryFavourite.equalTo("item", item)
queryFavourite.equalTo("school", school)
queryFavourite.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var objectFav = results[i];
var user = objectFav.get('user');
var userID = user.id;
var realItem = objectFav.get('item');
console.log(objectFav.get('user'));
console.log(objectFav.get('item'));
var UserClass = Parse.Object.extend("User");
var queryUser = new Parse.Query(UserClass);
queryUser.get(userID, {
success: function(userResult) {
console.log(userResult.get('school'));
console.log('install:' + userResult.get('installation').id);
var userInstallationId = userResult.get('installation').id;
var queryInstallation = new Parse.Query(Parse.Installation);
queryInstallation.equalTo('objectId', userInstallationId);
queryInstallation.find({
success: function(results) {
console.log('number' + results.length);
Parse.Push.send({
// deviceType: [ "ios" ],
where: queryInstallation,
data: {
alert: realItem + " is being served at " + diningLocation + " for " + meal
}
},
{
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
},
error: function(error) {
console.log('error');
}
});
},
error: function(error) {
console.log('error');
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
});
As you can see it is quite long and not very nice looking, I tried to save items to an array so to avoid sending two or more notifications but couldn't get that to work.
So I started writing another script that uses promises which looks much nicer but haven't gotten it all the way right now, it can match the items to users that have an item favourited and put the objectId's of those users in an array. Here is that code.
Parse.Cloud.define("test", function(request, response) {
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryFavourite = new Parse.Query(UserFavourite);
var userArray = [];
var TestItem = Parse.Object.extend("TestItem");
var query = new Parse.Query(TestItem);
query.limit(1000);
query.equalTo('school', 'Union College (NY)');
query.find().then(function(results) {
return results;
}).then(function(results) {
var promises = [];
for (var i = 0; i < results.length; i++) {
var object = results[i];
var item = object.get('item');
var school = object.get('school');
var meal = object.get('meal');
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryUser = new Parse.Query(UserFavourite);
queryUser.equalTo("item", item);
queryUser.equalTo("school", school);
var prom = queryUser.find().then(function(users) {
for (var i = 0; i < users.length; i++) {
var user = users[i];
var userID = user.get('user').id;
if (userArray.indexOf(userID) === -1) {
userArray.push(userID);
}
}
return userArray;
});
promises.push(prom);
}
return Parse.Promise.when.apply(Parse.Promise, promises);
}).then(function(results) {
console.log(userArray);
});
});
But now with this code I don't know where to go, I think using promises and such is the right way to go but I am now confused as once I have all the users that have an item favourited what to do, I then need to get there items that are favourited and are available in the TestItem class, this is where I am struggling.
Here is a pic of my UserFavourite class it has a pointer to the user who favorited the item as you can see, and also a user has more than one favorite.
Thanks a bunch for the help in advance.
Here is your code, and I changed a couple things.
Parse.Cloud.define("getAllFavoriteItems", function (request, response) {
var TestItems = Parse.Object.extend("TestItems");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItems);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', 'Union College (NY)');
userFavoritesQuery.include('testItems'); //This makes sure to pull all of the favorite item data instead of just the pointer object
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.limit(1000); //limit results to 1000
userFavoritesQuery.ascending('userId'); //group the user id's together in your array
userFavoritesQuery.find({
success:function(results) {
var pushNotificationMessage = "";
var userId = "";
for (var i=0; i <results.length; i++) {
if (results[i].get('userId') != userId) {
if (results[i].get('userId') != "") {
//TODO send push notification
}
userId = results[i].get('userId');
pushNotificationMessage = ""; //start a new push notification
}
pushNotificationMessage += results[i].get('item').get('name') + ": " + results[i].get('item').get('location') + "\n";
//SOMEWHERE BEFORE HERE I NEED THE INSTALLATION ID OF THE USER
//TO SEND THE PUSH TO THAT USER
Parse.Push.send({
// deviceType: [ "ios" ],
where: queryInstallation,
data: {
alert: pushNotificationMessage
}
},
{
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
}
response.success(true);
},
error:function(error) {
response.error();
}
})
});
Some code that might create push per user, rough outline though
if (i > 0) {
if (results[i].get('user') === results[i-1].get('user')) {
userItems.push(results[i]);
}
else {
userItems.length = 0;
}
}
else {
userItems.push(results[i]);
}
Not sure let me know if you understand what I'm trying to do...
So it a user has two items favourited I want it to group that into one, phrase that says what and where both items are being served
And here is code to send push
Parse.Push.send({
// deviceType: [ "ios" ],
where: queryInstallation,
data: {
alert: pushNotificationMessage
}
},
{
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
It can also be done with then/ promises,
I agree with #Maxwell that your UserFavorite should have links to both User and TestItem. This makes it possible to make your cloud-function as simple as:
Parse.Cloud.define("getAllFavoriteItems", function(request, response) {
var TestItem = Parse.Object.extend("TestItem");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItem);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', request.params.school);
userFavoritesQuery.include('testItem');
userFavoritesQuery.include('user');
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.find().then(function(results) {
var alerts = {};
for(var i =0 ; i<results.length; i++ ){
var user = results[i].get('user');
var testItem = results[i].get('testItem');
if(user && testItem){
var instId = user.get('installationId');
if(!alerts[instId]) {
alerts[instId] = [];
}
var m = results[i].get('item') + " is being served at {{diningLocation}} for " + testItem.get('meal');
alerts[instId].push(m);
}
}
response.success(alerts);
}, function(error) {
response.error();
});
});
This is working code that you can also find in my github repo.
You can also see the working demo here
The idea is the same as in Maxwell's answer: to have link in UserFavorites class to both User (where installationId is located) and TestItem entities. I've just made it working by including user and testItems properties in query, so when the result is returned filtered by school name I already have a list of installationIds.
Here is my schema:
User
TestItem
UserFavorites
Update:
In this code I added push notifications:
Parse.Cloud.define("getAllFavoriteItems", function(request, response) {
var TestItem = Parse.Object.extend("TestItem");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItem);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', request.params.school);
function SendPush(installationId, msg) {
var query = new Parse.Query(Parse.Installation);
query.equalTo('objectId', installationId);
Parse.Push.send({
where: query,
data: {alert: msg}
});
}
userFavoritesQuery.include('testItem');
userFavoritesQuery.include('user');
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.find().then(function(results) {
var groupedAlerts = {};
// manually iterating though results to get alert strings ang group by user in groupedAlerts[installationId]
for(var i =0 ; i<results.length; i++ ){
var user = results[i].get('user');
var testItem = results[i].get('testItem');
if(user && testItem){
var instId = user.get('installationId');
if(!groupedAlerts[instId]) {
groupedAlerts[instId] = [];
}
var m = results[i].get('item') + " is being served at {{dining Location}} for " + testItem.get('meal');
groupedAlerts[instId].push(m);
}
}
// reformat to array and send push notifications
var alerts = [];
for(var key in groupedAlerts) {
alerts.push({
installationId: key,
alerts: groupedAlerts[key],
});
// Send push notifications
SendPush(key, groupedAlerts[key].join());
}
response.success(alerts);
}, function(error) {
response.error();
});
});
I've also updated test data in live demo (just press Get Alerts) or feel free to play around with test data hot it changes cloud code response. gitnub repo is also up to up to date.
This is based on what I understand as the problem you're trying to solve. If it's not addressing the right issue, let me know and I'll see what I can do.
Looking first at your database model, we can simplify this a bit by modifying the UserFavorites table. Starting with the initial two classes, you have a table of items and a table of users. Since a user can favorite many items and an item can be favorited by many users, we have a many-to-many relationship that exists. When this happens, we need to make a third class that points to each of the other two classes. This is where the UserFavorites table comes into play. In Parse terms, the UserFavorites table needs to have two pointers in it: one for the user and one for the item.
Once the UserFavorite table exists with it's two pointers, we can do a few things fairly easily. In your case, we have a few searching criteria:
each item must be at a given school
you want to limit your responses to the first 1000
To accomplish this you can combine two queries into one by calling matchesQuery.
Parse.Cloud.define("getAllFavoriteItems", function (request, response) {
var TestItems = Parse.Object.extend("TestItems");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItems);
var userQuery = new Parse.Query(Parse.User);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', 'Union College (NY)');
userQuery.include('Installation');
userFavoritesQuery.include('testItems'); //This makes sure to pull all of the favorite item data instead of just the pointer object
userFavoritesQuery.include('User'); //This makes sure to pull all of the favorite item data instead of just the pointer object
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.matchesQuery('user', userQuery); //This will run the third query against Users, bringing the installation data along with it
userFavoritesQuery.limit(1000); //limit results to 1000
userFavoritesQuery.ascending('userId'); //group the user id's together in your array
userFavoritesQuery.find({
success:function(results) {
...
},
error:function(error) {
response.error();
}
})
})
Once we get that far, then compiling the push message for each user should be a matter of straight-forward string parsing logic. For example, in the success function, one way we can extract the data we is this:
success:function(results) {
var pushNotificationMessage = "";
var userId = "";
for (var i=0; i <results.length; i++) {
if (results[i].get('userId') != userId) {
if (results[i].get('userId') != "") {
//TODO send push notification
}
userId = results[i].get('userId');
pushNotificationMessage = ""; //start a new push notification
}
pushNotificationMessage += results[i].get('item').get('name') + ": " + results[i].get('item').get('location') + "\n";
}
response.success(true);
}
I haven't tested these examples to see if they'll work, but I hope this gives you an idea of how to simplify your queries into something a little more manageable.