Javascript objects - feature missing from overall object, yet exists - javascript

Strange problem here. I'm running Node/Express/Mongoose/Leaflet. I pull an array of locations from my db, and once the callback is initiated, I iterate over those locations to find a bunch of string passages that deals with each location. I then try to append the array of passages to each location object, and then append the locations array to a GeoJSON FeatureCollection.
Location.find({}, { _id: 0 }, function (err, locations) {
if (err) {
console.log('DB Error loading all locations');
res.redirect('/');
} else {
var num = 0;
console.log("Beginning finding all passages");
locations.forEach(function (location) {
num++;
console.log("Looking up a location");
Passage.find({"placekey": location.properties.placekey}, function (err, passages) {
if (err) {
console.log('DB Error finding passage for: ' + location.properties.placekey);
} else {
console.log("Passage was found!");
location.properties.passages = passages[0]; //take first passage
num--;
}
if (num === 0) {
console.log("All passages were found!");
var featureCollection = {
"type": "FeatureCollection",
"features": locations
};
console.log(featureCollection);
console.log(featureCollection.features[0].properties);
console.log(featureCollection.features[0].properties.passages);
res.json(featureCollection);
console.log("JSON sent over!");
}
});
});
Logging the featureCollection gets me my featureCollection without any passages:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"num_books": 62,
"Age": "Built 1078",
"ID": "",
"num_mentions": 325,
"Place": "The Tower",
"placekey": "TheTower",
"GeocodeNotes": "",
"Notes": "Built on the site of Roman fortifications, the central part of the Tower, known as the White Tower, was built in 1078 by William the Conqueror. Subsequent rings of fortification were added later. It was used as a royal residence as well as a prison and place of execution until Elizabethan times. England's child king, Edward V, and his brother were murdered in the Tower in 1483 supposedly by their uncle, Richard III.",
"Class": "n/a",
"Type": "Landmark"
},
"geometry": {
"type": "Point",
"coordinates": [
-0.076111,
51.508056
]
}
},
// more objects
No passages property.
However, when I use console.log(featureCollection.features[0].properties.passages), I get the first passage:
{
"_id": "51deebdbb2b5de1b8b6d7da1",
"index": 27100,
"bookid": 1,
"author": "Ainsworth, William",
"place": "The Tower",
"placekey": "TheTower",
"query_ok": true,
"year": 1839,
"corpus": "Chadwyck",
"fn": "/Volumes/LilaData1/Plain2/Chadwyck/lilaBookId_00149.txt",
"context_a": "The course of the carpenter's meditations was here...
//more features
}
Moreover, using (if 'passages' in featureCollection.features[0].properties) gives me true. In fact, I can condition sending a JSON response from the server to that, and my featureCollection without passages will be sent...
Sorry for the long-winded post, but I'm really going crazy over this. Any ideas?
Thank you! :)

The problem is that the inspect defined on Document is interfering with console.log operation. This inspect does not consider the properties added to the document instance (like documentinst.prop=1).
To fix your problem try to use toJSON on the return documents and then attach properties to the return object
In your case,
var _locations = [];
locations.forEach(function(_location){ // actual mongoose document
var location;
location = _location.toJSON(); // do toJSON on the _location mongoose document
_locations.push(location); //push this object into the new array _locations
....logic for passages...
//imp - add properties to the location object return from toJSON
if (num === 0) {
...
var featureCollection = {
"type": "FeatureCollection",
"features": _locations // use the new _locations array
};
...
}
});

When res.json(obj) is called, the first thing this method does is to call JSON.stringify(obj);
If the obj has toJSON defined , the object returned from invoking obj.toJSON() is used by the JSON.stringify.
If you provide a toJSON method on an object and if it is coded to return another object
and not the object on which toJSON is defined, JSON.stringify will operate on this new object.
And you will be able to view the properties of this new object and not the actual object.
res.json relies on toJSON.
console.log relies on inspect.
I just demonstrated the above with this code snippet,
var util = require('util');
function Document(){this._doc = {};}
Document.prototype.inspect = function(){return util.inspect(this._doc)};
Document.prototype.toJSON = function(){return this.inspect()};
var docInst = new Document();
docInst.newProp = 'abc'; //property never shows up in console.log / JSON.stringify
docInst._doc._newProp = "_abc";
console.log(docInst);
JSON.stringify(docInst);

Related

Discord Bot - Downloading JSON file from a website and extract certain elements of it

I'm working on a command that will automatically fetch a file from a link once a day and extract two of the elements in it and send that as a message in a channel.
My issue here is that I'm having issues actually getting the file downloaded. I've been trying several different functions to fetch the file but nothing has worked so far. I have attached one of the functions I've tried below.
async function getQuote () {
const url = "https://quotes.rest/qod?category=inspire";
const path = Path.resolve(__dirname, 'temp', 'qod.json')
const writer = fs.CreateWriteStream(path)
const response = await axios({
url,
method: 'GET',
responseType: 'stream'
})
response.data.pipe(writer)
getQuote();
return new Promise((resolve, reject) => {
writer.on('finish', resolve)
writer.on('error', reject)
})
}
fs.readFile('./temp/qod.json', 'utf8', function (err, data) {
if (err) throw err;
var obj = JSON.parse(data);
msg.channel.send(data);
})
The file I'm trying to work with here looks something like this:
{
"success": {
"total": 1
},
"contents": {
"quotes": [
{
"quote": "What you do speaks so loudly that I cannot hear what you say.",
"length": "61",
"author": "Ralph Waldo Emerson",
"tags": [
"action",
"inspire",
"leadership",
"management",
"tod"
],
"category": "inspire",
"language": "en",
"date": "2020-08-23",
"permalink": "https://theysaidso.com/quote/ralph-waldo-emerson-what-you-do-speaks-so-loudly-that-i-cannot-hear-what-you-say",
"id": "eZ0NtMPtGp8c5eQJOBfJmweF",
"background": "https://theysaidso.com/img/qod/qod-inspire.jpg",
"title": "Inspiring Quote of the day"
}
]
},
"baseurl": "https://theysaidso.com",
"copyright": {
"year": 2022,
"url": "https://theysaidso.com"
}
}
It wants to download as a json file, but when visiting the link, it is listed as a xml document.
How would I go about getting this downloaded and extracting two lines from it? If you're wondering, the two lines are the quote and author lines.
Thanks!
I copy your code and run my local machine and everythin fine.
Limitations are like mirages created by your own mind. When you realise that limitation do not exist, those around you will also feel it and allow you inside their space. - Stephen Richards
Looks like you are trying to write the result to a file and then read from the file which is not efficient. Here's a much simpler way of doing it.
async function getQuote() {
const url = "https://quotes.rest/qod?category=inspire";
const response = await axios(url);
const result = response.data;
/*
result =
{
"success": {
"total": 1
},
"contents": {
"quotes": [
{
"quote": "Limitations are like mirages created by your own mind. When you realise that limitation do not exist, those around you will also feel it and allow you inside their space. ",
"length": "171",
"author": "Stephen Richards",
"tags": [
"inspire",
"motivational",
"positive-thinking",
"self-empowerment",
"self-help",
"self-improvement",
"wealth",
"wealth-creation"
],
"category": "inspire",
"language": "en",
"date": "2020-08-24",
"permalink": "https://theysaidso.com/quote/stephen-richards-limitations-are-like-mirages-created-by-your-own-mind-when-you",
"id": "OLSVpLiSwrWplvCcFgPPiweF",
"background": "https://theysaidso.com/img/qod/qod-inspire.jpg",
"title": "Inspiring Quote of the day"
}
]
},
"baseurl": "https://theysaidso.com",
"copyright": {
"year": 2022,
"url": "https://theysaidso.com"
}
}
*/
//this is an array of quote objects
const quotes = result.contents.quotes;
//extracting first quote object from the array
const quoteObject = quotes[0];
//extracting quote text and author from quote object
const quote = quoteObject.quote;
const author = quoteObject.author;
//the >>> will make it look like a quote in discord.
console.log(`>>> ${quote}\n- ${author}`);
//send the formatted quote to the channel
msg.channel.send(`>>> ${quote}\n- ${author}`);
//if for some reason you want to save the result to a file
fs.writeFile(filePath, result, function(err) {
if (err) throw err;
console.log('Saved!');
});
}
getQuote();
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
I would suggest simply reading the quote to an object, then creating a string using interpolation and send it on the discord channel:
async function getQuote () {
const url = "https://quotes.rest/qod?category=inspire";
console.log("getQuote: Reading quote...");
// Get the response as an object
const response = await axios({
url,
method: 'GET'
})
// Use destructuring to get the quote and author
let { quote, author } = response.data.contents.quotes[0];
// Format our quote
let data = `${quote} - ${author}`;
// Add a console.log for debugging purposes..
console.log("getQuote: Sending quote:", data);
// Send the quote on the channel
msg.channel.send(data);
}
Todays quote would then look like so:
Limitations are like mirages created by your own mind. When you realise that limitation do not exist, those around you will also feel it and allow you inside their space. - Stephen Richards

I am not get value which is sent in request body in specific json format

I need help in my one issue. I have write the program in that I am use map in node.js.
I am testing this program using postman by sending JSON structure, however I am not get specific value in console which I am printing.
Please see below code .
async CreateProduceMVPRateAsset(data, callback) {
// Create a new file system based wallet for managing identities.
var ProducePRICE = {};
var MVPRATE = new Map();
var MVPPRICE =[];
var MVPPRICE_BS ={};
var MVPPRICE_LB ={};
var PRODUCENAME = data.PRODUCE
console.log('PRODUCENAME', PRODUCENAME);
var COUNTRY = data.COUNTRY;
console.log('COUNTRY', COUNTRY);
var STATE = data.STATE;
console.log('STATE', STATE);
MVPRATES = data.MVPRATES;
console.log('MVPRATERATE', MVPRATES); // not getting value of MVPRATES from request body
}
JSON structure which is sending using POSTMAN
{
"username": "admin2",
"PRODUCE": "Apple",
"STATE": "MI",
"COUNTRY": "US",
"MVPRATES": {
"fuji": {
"VARIETY": "fuji",
"RATE": [
{
"UNIT": "Bussel",
"CURRENCY": "USD",
"VALUE": 10.25,
"UIDISPLAY": true
}
]
},
"gala": {
"VARIETY": "gala",
"RATE": [
{
"UNIT": "Bussel",
"CURRENCY": "USD",
"VALUE": 10.25,
"UIDISPLAY": true
}
]
}
}
}
output
Any help very appreciable
Thanks
Abhijeet
That's how logs will show up for the non-primitive type of data. Try stringifying the response like:
MVPRATES = data.MVPRATES;
console.log('MVPRATERATE', JSON.stringify(MVPRATES));
This will help you in printing actual values to the logs. A better approach will be to use a logging module like winston and configure all such things and many more.
Sorry to waste you all time I think I miss the var in front of MVPRATES. It should be
var MVPRATES = data.MVPRATES;

Adding a new object into a nested array

I want to add a new object for each nested array. I'm calling this function any time I add a product to my orderintake:
add2order(productID, productName, productRatePlans) {
this.orderIntake.push({ productID, productName, productRatePlans });
let i = 0;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges.forEach(element => {
i++;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].quantity = this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].defaultQuantity;
});
}
this is an example response from the server:
{
"id": "8adc8f996928b9a4016929c59b943a8f",
"sku": "SKU-00006778",
"Partner_Account_ID__c": null,
"productRatePlans": [
{
"id": "8adce4216928c28d016929c59bff3372",
"status": "Active",
"name": "Enterprise",
"description": null,
"effectiveStartDate": "2016-02-26",
"effectiveEndDate": "2029-02-26",
"productRatePlanCharges": [
{
"id": "8adc8f996928b9a4016929c59d183a92",
"name": "USAGE_COUNTER_2",
"type": "Usage",
"model": "Volume",
"uom": "Each",
"pricingSummary": [
"Up to 5000 Each: USD0 flat fee"
],
"pricing": [
{
...
}
],
"defaultQuantity": null,
"applyDiscountTo": null,
"discountLevel": null,
"discountClass": null,
...
"financeInformation": {
..,
}
}
]
}
],
"productFeatures": [
{
...
}
]
}
The data is being retrived this way from an external REST backend so unfortunately I can't initialize the data including the new property...
so in every productRatePlanCharges there should be 1 new object 'quantity'.
How can I add this field to every productRatePlanCharges?
Right now I'm getting: ERROR
TypeError: Cannot read property 'productRatePlanCharges' of undefined
And how can I make sure I'm always adding this to the last orderIntake element? Don't mind productRatePlans there is only 1 in each orderintake...
thanks for your support!
Here you have to create productDetails object with inititalised array like below so that you won't get the error.
add2order(productID, productName, productRatePlans) {
// Create object like below
let productDetails = { productID : productID, productName : productName, productRatePlans : productRatePlans
}
this.orderIntake.push(productDetails);
let i = 0;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges.forEach(element => {
i++;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].quantity = this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].defaultQuantity;
});
}
Hope this will help!
as you used Angular you probably use Typescript too. I recommend that you create a model like your incoming model and there define your quantity: number inside productRatePlanCharges object. then map the incoming data to your own model. therefore you will have a quantity=0 in your model that you can change it later in a loop.
If you want to continue with your own way take a look at this:
Add new attribute (element) to JSON object using JavaScript
there is no problem to add an element to current model almost like you did, and the problem might be somewhere else as your error refers to existence of productRatePlanCharges!
as you used forEach I prefer to use that 'element' and double iterating with i++; is not a good idea to me.
this might be better:
element.quantity = element.defaultQuantity;

How to get properties from feature without event listener? (event.feature)

how to get the "feature" data from a layer (loaded with geoJSON) without event listener?
thats the usual way, which works fine:
layer1.addListener('mouseover', function (event) {
console.log( event.feature.getProperty('description') );
}
but now i want to get the value from data object "layer1".
i tried already this:
layer1.getProperty('description')
layer1.feature.getProperty('description')
The way how i load the geoJSON
var layer1 = new google.maps.Data();
layer1 = loadGeoJson('https://googledrive.com/host/MYFILE')
The content of json
Here a short question btw: if i have more features (e.g polygons) in my json, can i get this values to manipulate (e.g. toggle visibility) it?
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
12.3456789,
01.2345678,
0
]
},
"properties": {
"name": "Test Polygon",
"description": "My Test Description"
}
}
]
}
To get all the features of a google.maps.Data Layer, use:
forEach(callback:function(Data.Feature))
Return Value: None
Repeatedly invokes the given function, passing a feature in the collection to the function on each invocation. The order of iteration through the features is undefined.
or to get a single feature (if you know the Id):
getFeatureById(id:number|string)
Return Value: Data.Feature|undefined
Returns the feature with the given ID, if it exists in the collection. Otherwise returns undefined.
Note that the IDs 1234 and '1234' are equivalent. Either can be used to look up the same feature.

KnockoutJs - Pushing to an element in an observable array does not work

I have an object (analysisLogData) that I use to generate a table using KnockoutJS. Here's the viewModel containing this object:
function AppViewModel() {
var self = this;
self.analysisLogData = ko.observableArray();
self.analysisLogTitle = ko.observable("Warnings")
self.changeAnalysisLog = function(title) {
self.analysisLogTitle(title)
}
var data =
{
"Warnings": [
{
"number": 3002,
"description": "There may be a problem with the device you are using if you use the default profile"
},
{
"number": 3001,
"description": "There may be a problem with the device you are using if you don't use the default profile"
}
]
,
"Errors": [
{
"number": 1000,
"description": "No networks are loaded"
},
{
"number": 1002,
"description": "No devices are loaded"
}]
}
self.addLog = function (type, content) {
self.analysisLogData()[type].push(content);
}
self.analysisLogData.push(data)
}
ko.applyBindings(new AppViewModel());
You can see the result here in a JSFiddle: http://jsfiddle.net/etiennenoel/V4r2e/5/
I want to be able to add an error or a warning without losing the warnings or errors already present.
I tried to do the following in the self.addLog function:
self.addLog = function (type, content) {
self.analysisLogData()[type].push(content);
}
but it says that it can't push to an undefined object...
Ok, after playing around in fiddle. I believe that you need to do some changes in how you pushed data in the observable array. But without doing a lot of modification check my solution in this link.
jsfiddle example
self.addLog = function (type, content) {
self.analysisLogData()[0][type].push({
"number": 1002,
"description": content
});
}
And data object should be
"Warnings": ko.observableArray([........]),
"Errors": ko.observableArray([..........])
I did two things
Modify Warnings & Errors to be an Observable Array
I pushed the data in this self.analysisLogData()[0][type].push instead of self.analysisLogData()[type].push
self.analysisLogData() is an array which contains arrays of Errors/Warnings.
I'm not sure if that's how you want your data structured.
To get the fiddle to work you can replace the addLog function with this:
self.addLog = function (type, content) {
self.analysisLogData()[0][type].push(content);
}

Categories