parsing an array of JSON objects - null value preventing upsert - javascript

I am parsing an array of objects, and there's about 12 fields in each. I am doing this with two nested for(var i = 0; i < array.length; i++) functions, and ran into a null field several responses in.
I am expecting to get an embedded object, ... "caption": {"id":"some id", "text":"some text"}, but am instead getting a null in some cases. My schema does not require the field to have a value, but the document is getting kicked out.
How can I get around this? Would expect the null just to insert a blank value, but that is not the case. I am working within the Meteor.js framework, but the snippet I need help with below is just plain old javascript and a mongodb upsert. The error is on the caption.text line.
Meteor.methods({
'getMethod': function (var1, var2) {
check (var1, Number);
this.unblock();
var url = "https://www.url.com/"+var1+"/stuff?"+token+"&param="+var2;
var result = HTTP.call("GET", url);
if(result.statusCode===200) {
var aArray = JSON.parse(result.content).data;
for(var i = 0; i < aArray.length; i++){
var id = aArray[i].id;
var aItem = {
_id: aArray[i].id,
userId: aArray[i].user.id,
username: aArray[i].user.username,
created_time: parseInt(aArray[i].created_time),
type: aArray[i].type,
caption: aArray[i].caption.text, // THIS LINE IS THROWING THE ERROR !!
}
Collection.upsert(id, {$set: aItem}, {validationContext: 'upsertForm'}, function(error, result) {
if (error){
console.log("Error:", error);
return aArray;
}
});
}
} else {
console.log("Error: ", result.statusCode);
var errorJson = JSON.parse(result.content);
throw new Meteor.Error(result.statusCode, errorJson.error);
}
},
});

Use a ternary to check if there's a text property on the caption:
caption: (aArray[i].caption.text) ? aArray[i].caption.text : ''
Edit : If the caption property is questionable, then use the following:
caption: (aArray[i].caption) ? aArray[i].caption.text : ''
h/t RobG

Related

Creating a JS object

I am working with the Wordpress REST API to retrieve some tags from a website. Maybe I haven't searched for the correctly on SO, but I am trying to get use my data and create my dataSet variable. But I am getting an error saying it's not recognizing the for loop token. I am quite new to Javascript so I don't know how to fix this.
Here is my code:
var req = new XMLHttpRequest();
var pieChart = document.getElementById('pie_chart_1');
req.open("GET", "http://example.org/wp-json/wp/v2/tags?per_page=10")
req.onload = function() {
if (req.status >= 200 && req.status < 400) {
var data = JSON.parse(req.responseText);
} else {
console.log("Returning an error");
}
};
req.onerror = function() {
console.log("Connection error");
};
req.send();
var dataSet = [
for(i = 0; i < data.length; i++) {
{legendLabel: data[i].name, magnitude:data[i].count, link: "http://example.com/tag" + data[i].slug},
}
];
You have a for loop inside the square brackets to define an array which is not syntactically correct. Since you are fetching tags using the WordPress REST API the response will be a JSON object with a tags property containing the results. It looks like you have a missing / in your link value as well.
Assuming this value for data.tags -
[{
display_name: 'Test Name',
slug: 'test-name',
}];
To properly use a for loop for this purpose -
const dataSet = [];
const tags = data.tags;
for (let i=0; i<tags.length; i+=1) {
dataSet.push({
legendLabel: tags[i].display_name,
link: "http://example.com/tag" + tags[i].slug,
});
}
A better way to create an array of JSON objects from your original array of JSON objects is to use Array.map() to "map" each element in the first array to a new element in a new array -
const dataSet = data.tags.map(function (element) {
return {
legendLabel: element.display_name,
link: "http://example.com/tag/" + element.slug,
};
});
Taking it further, you can use an arrow function, object parameter deconstruction, explicit return, string patterns, etc for more compact syntax with the same functionality -
const dataSet = data.tags.map(({ display_name: displayName, slug }) => ({
legendLabel: displayName,
link: `http://example.com/tag/${slug}`,
});
First of all, in most cases, it is better to use fetch API
In JS there is no for comprehensions so the last block of code could be refactored as follows:
const dataSet = data.map(({ name, count, slug }) => ({
legendLabel: name,
magnitude: count,
link: `http://example.com/tag${slug}`
}));

Get parent object from id that matches objects child array with mongoose in NodeJS

I'm working with mongoose in NodeJS and I have an id from a child array. I the models are defined like this:
var thingSchema = new schema({
thingId: String,
smallerThings : [smallerThingsSchema]
});
var smallerThingsSchema = new schema({
smallerThingId: String,
name : String,
anotherName : String
});
I have the smallerThingId, but I want to get the thingId.
Right now I have a for loop that looks like this (pretty ineffective I suppose). Potentially, there could be 100,000 things.
//Find all things
thingModel.find({}, null, function (error, things) {
if (error) {
callback(error);
}
else {
//Go through all things
for(var i = 0; i < things.length; i++){
//check if a thing with the array of smaller things matches the id
for(var j = 0; j<things[i].smallerThings.length; j++){
if(things[i].smallerThings[j].id === smallerThingId){
//return it
return things[i];
}
}
}
}
});
Grateful for any help or where I can look (docs/blog/other) to learn how to handle such scenario.
To get document by sub-document id you can use following code:
thingModel.find({"smallerThings.smallerThingId":smallerThingId}, null, function (error, things) {
});
This will return document having "smallerThingId".
You can use mongoose find like:
thingModel.find({"things.smallerThings.id": smallerThingId }, null, function (error, things) {
if (error) {
callback(error);
}
else {
//do what you need
}
});

how to add key and value to array that is mongodb find result (added key/value gets lost)

This must be a standard Javascript problem, but my searches here did not turn up any results that were applicable to me.
I am querying a mongodb database and want to add key/values to the objects in the result array through the enrichJSON function.
The MongoBooks.find returns some documents in an Array docs. Probably enrichJSON is a bad name, it should be something like enrichArray, it is arbitrary anyway.
function enrichJSON(jsonobj) {
for (var i = 0; i < jsonobj.length; i++) {
jsonobj[i].myparam = "something meaningful";
}
return jsonobj;
}
MongoBooks.find(mongoquery, function (err, docs) {
if (err) throw err;
var step1 = docs;
var step2 = enrichJSON(step1);
console.log("step1:" + step1);
console.log("step2:" + step2);
});
The step2 console output is missing the myparam output. Same goes if I try
jsonobj[i]["myparam"] = "abctest";
in the enrichJSON.
I am getting
step1: {[{"title":"good book"}]}
step2: {[{"title":"good book"}]}
but would like to get
step2: {[{"title":"good book", "myparam":"something meaningful"}]}
Edit to give an actual (stripped down) example of my result (I edited to make it simpler but probably mixed up the brackets):
step2:{ _id: 5474e8a35e79556ced436700,
isbn10: '0370303466',
author: 'Graham Greene',
lang: 'eng',
title: 'Travels with my aunt'
}
I am still missing what I added (myparam).
You have a broken response.
{[{"title":"good book"}]}
should be something like this :
{'data' : [{"title":"good book"}]}
Then your enrich function would look like this :
function enrichJSON(jsonobj) {
for (var i = 0; i < jsonobj.data.length; i++) {
jsonobj.data[i].myparam = "something meaningful";
}
return jsonobj;
}

How to serialize delete data with jqGrid, multiselection, and Spring?

Currently, I have an overridden delGridRow call that looks like this (credit to Krams and his Spring tutorial):
var row = $('#grid').jqGrid('getGridParam','selrow');
$('#grid').jqGrid( 'delGridRow', row,
{ url:'deleteRequirement.html',
recreateForm: true,
beforeShowForm: function(form) {
//Change title
$(".delmsg").replaceWith('<span style="white-space: pre;">' +
'Delete selected record?' + '</span>');
//hide arrows
$('#pData').hide();
$('#nData').hide();
},
reloadAfterSubmit:true,
closeAfterDelete: true,
serializeDelData: function (postdata) {
var rowdata = $('#grid').getRowData(postdata.id);
// append postdata with any information
return {id: postdata.id, oper: postdata.oper, reqID: rowdata.reqID};
},
afterSubmit : function(response, postdata)
{
var result = eval('(' + response.responseText + ')');
var errors = "";
if (result.success == false) {
for (var i = 0; i < result.message.length; i++) {
errors += result.message[i] + "<br/>";
}
} else {
$('#msgbox').text('Entry has been deleted successfully');
$('#msgbox').dialog(
{ title: 'Success',
modal: true,
buttons: {"Ok": function() {
$(this).dialog("close");
}
}
});
}
// only used for adding new records
var newId = null;
return [result.success, errors, newId];
}
});
else {
$('#msgbox').text('You must select a record first!');
$('#msgbox').dialog(
{ title: 'Error',
modal: true,
buttons: {"Ok": function() {
$(this).dialog("close");}
}
});
}
In order to add support for multiselection deletes, I changed the "selrow" first line to this:
var rowList = jQuery("#grid").getGridParam('selarrrow');
After this, things start getting sketchy fast. The spec says that the default delGridRow can accept an array of inputs records to delete. I made the following change to attempt to get the new 'rowList' variable to get used:
$('#grid').jqGrid( 'delGridRow', rowList, ...
I'm still hitting my deleteRequirement.html URL in my Spring controller, but only the last records appears to make it. I'm guessing the problem is in the postdata preparation in the serializeDelData section, but I haven't found the correct way to prepare this postdata with the list of records instead of the single record.
Any suggestions/insight would be appreciated.
Thanks all.
I don't use Spring myself, but some parts of your code seams be strange for me.
First of all the you can use two forms of the first parameter of delGridRow (row in your code). It can be either the comma-separated list of ids or an array of ids. If you use array of ids then jqGrid convert it to the comma-separated format by rowids = rowids.join();. As the result the format of postdata.id inside of serializeDelData can be also the comma-separated list of ids.
So if you need to support delete of multiple rows you should
modify the code of serializeDelData to send in reqID property also the list of the reqID. The corresponding code can be
serializeDelData: function (postdata) {
var ids = postdata.id.split(','), i, l = ids.length, reqIDList = [];
for (i = 0; i < l; i++) {
reqIDList.push($(this).jqGrid("getCell", ids[i], "reqID"));
}
return {id: postdata.id, oper: postdata.oper, reqID: reqIDList.join()};
}
modify your server code to support both id and reqID in comma-separated form.
Inside of afterSubmit callback you you the lines
// only used for adding new records
var newId = null;
return [result.success, errors, newId];
You can modify the lines to the following
return [result.success, errors];
because only the first two elements of the array returned by afterSubmit callback will be used.

Handling no results in jquery autocomplete

Hey I'm trying to return a message when there are no results for the users current query! i know i need to tap into the keyup event, but it looks like the plugin is using it
This question is really out of date, anyways I'm working with the new jQuery UI 1.8.16, autocomplete is now pretty different:http://jqueryui.com/demos/autocomplete/#default
Anyways if you're trying to the do the same thing as the question asks, there is no more parse function, as far as I know there is no function that is called with the search results.
The way I managed to pull this off is by overriding the autocomplete's filter function - Note: this will affect all your autocompletes
$.ui.autocomplete.filter = function(array, term) {
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
var aryMatches = $.grep( array, function(value) {
return matcher.test(value.label || value.value || value);
});
if (aryMatches.length == 0){
aryMatches.push({
label: '<span class="info" style="font-style: italic;">no match found</span>',
value: null
});
}
return aryMatches;
};
The function is slightly modified from the source, the grep call is the same, but if there are no results I add an object with a value of null, then I override the select calls to check for a null value.
This gives you an effect where if you keep typing and no matches are found you get the 'no matches found' item in the dropdown, which is pretty cool.
To override the select calls see jQuery UI Autocomplete disable Select & Close events
$(this).data('autocomplete').menu.options.selected = function(oEvent, ui){
if ($(ui.item).data('item.autocomplete').value != null){
//your code here - remember to call close on your autocomplete after
}
};
Since I use this on all my autocompletes on a page, make sure you check if value is null first! Before you try to reference keys that aren't there.
You could try supplying a parse option (function to handle data parsing) and do what you need when no results are returned to parse.
This example assumes you're getting back an array of JSON objects that contain FullName and Address attributes.
$('#search').autocomplete( {
dataType: "json",
parse: function(data) {
var array = new Array();
if (!data || data.length == 0) {
// handle no data case specially
}
else {
for (var i = 0; i < data.length; ++i) {
var datum = data[i];
array[array.length] = {
data: datum,
value: data.FullName + ' ' + data.Address,
result: data.DisplayName
};
}
}
return array;
}
});
I'm using the following code for the same purpose (the message is shown in the autocomplete list):
success: function(data, status, xhr){
if(!data.length){
var result = [
{
label: 'There are no matches for your query: ' + response.term,
value: response.term
}
];
response(result);
}
else{
// normal response
}
}
You can also utilize the "response" event to examine this. Simple but powerful. http://api.jqueryui.com/autocomplete/#event-response
response: function (event, ui) {
if (ui.content.length == 0) {
//Display an alert or something similar since there are no results
}
},

Categories

Resources