Can I have a recursive JavaScript function inside a $.getJSON function? - javascript

I have a JSON document of variable depth. An example:
[
{
"type": "firsttype",
"text": {
"id": "content"
}
}
]
What I am trying to do is to retrieve the values of certain keys, say text. Since I do not know where these keys might appear in the .JSON, I need to use a recursive function. I then try to display these keys and values in a HTML file.
I have a preliminary attempt here:
$.getJSON("file.json", function getText (oValue, sKey) {
links = [];
if (typeof oValue == "object" || typeof oValue == "array") {
for (i in oValue) {
getText (oValue [i], i);
}
} else {
links.push ( "<li id='" + oValue + "'>" + sKey + "</li>" );
}
$( "<ul/>", {
"class": "my-new-list",
html: links.join( "" )
}).appendTo( "body" );
});
When I load the page locally or remotely or on python -m SimpleHTTPServer, I get no errors and nothing on the page. What am I doing wrong? I have included all the JS in the $.getJSON call so no async issues arise.
In the future I would also like to include a regexp check so I can extract values with a certain string, e.g. /http/. What would be the best way to do this?

As the other answers cover most of the things that you should consider, I think I'll just post a solution for your actual problem. :)
You want to traverse an arbitrary JSON and search for a specific key, and may have a condition on its value. Then you want to return all values for your specified key (that pass your condition if specified).
Consider you have the following json:
{
"hello": "some text",
"object": {
"key1": "hello!",
"key2": "Bye!"
},
"array": [{
"some_key1": "blah blah blah",
"some_key2": 24
}, {
"some_key1": "ghiojd",
"some_key2": 13
}],
"numeric_array": [2, 3, 4, 5]
}
This snippet will search the above json for some_key1 that its value starts with blah:
function regexpConditionFactory(regex) {
return function(value) { return regex.test(value); };
}
function searchObjectForKey(obj, key, condition, result) {
if ($.isPlainObject(obj)) {
if (obj.hasOwnProperty(key)) {
if (!condition || ($.isFunction(condition) && condition(obj[key])))
result.push(obj[key]);
}
}
if ($.isPlainObject(obj) || $.isArray(obj)) {
for (var k in obj) {
if (obj.hasOwnProperty(k))
searchObjectForKey(obj[k], key, condition, result);
}
}
}
$.getJSON('file.json', function(data) {
var res = [];
searchObjectForKey(data, 'some_key1', regexpConditionFactory(/^blah/), res);
$('<ul/>', {
'class': 'my-new-list',
'html': res.map(function(value) { return '<li>' + value + '</li>'; }).join('')
}).appendTo('body');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

I think your error is in the declaration of variable links :
it must be outside the recursive function i think.
inside the function it will be reinitialized every time.
var links = [];
$.getJSON("file.json", function getText (oValue, sKey) {
if (typeof oValue == "object" || typeof oValue == "array") {
for (i in oValue) {
getText (oValue [i], i);
}
} else {
links.push ( "<li id='" + oValue + "'>" + sKey + "</li>" );
}
$( "<ul/>", {
"class": "my-new-list",
html: links.join( "" )
}).appendTo( "body" );
});

Try moving your function definition outside of the event handler:
function getText (links, oValue, sKey) {
if (typeof oValue == "object" || typeof oValue == "array") {
for (i in oValue) {
getText (links, oValue [i], i);
}
} else {
if(oValue && sKey)
links.push ( "<li id='" + oValue + "'>" + sKey + "</li>" );
}
$( "<ul/>", {
"class": "my-new-list",
html: links.join( "" )
}).appendTo( "body" );
};
$.getJSON("file.json", function(data, success){
var links = [];
getText (links, data, 0);
});
Feel free to edit this if there are errors.
The aim is to pass the links array in to the recursive function, and to avoid mixing the named function definition with the getJSON, for clarity at least, and to ensure you can pass an initialized sKey.

You can, but in your case you probably shouldn't
You can pass a named function as a callback function — which is useful for recursion (See: Using Named Callback Functions In Javascript Methods, Ben Nadel) — but in your case, you probably shouldn't.
Based on your example, there are some things you will want to do before and after you invoke a recursive function. These things, such as declaring variables and appending elements to the body, will need to happen [once] outside of recursion.
So, pass an anonymous function as your callback that contains your recursive function definition and invokes it as needed.
In your case, you'll want to invoke it for each object returned in the JSON response array. So, you can put it inside of an iterator as well. Here I used jQuery's $.each() for convenience.
/* FUNCTION THAT MAKES JSON REQUEST */
function doJsonThing() {
/* BEGIN REWRITE OF OP's EXAMPLE CODE */
$.getJSON('file.json', function (json) {
var links = [];
function getText (key, val) {
if (typeof val === 'object' || typeof val === 'array') {
for (i in val) {
getText(i, val[i]);
}
} else {
links.push('<li id="' + val + '">' + key + ' (' + val + ')</li>' );
}
}
$.each(json, function (key, val) {
getText(key, val);
});
$('<ul/>', {
"class": "my-new-list",
"html" : links.join('')
}).appendTo('body');
});
/* END REWRITE OF EXAMPLE CODE */
}
/* THE FOLLOWING IS NOT PART OF THE EXAMPLE,
// IT IS JUST USED TO SIMULATE A SERVER RESPONSE */
/* BEGIN UNIT TEST */
// CREATE CLOSURE TO RETURN DUMMY FUNCTION AND FAKE RESPONSE
function json_response(response) {
return function (url, success) {
success(response);
};
}
// OVERRIDE $.getJSON WITH DUMMY FUNCTION AND FAKE RESPONSE
$.getJSON = json_response(
// SIMULATED CONTENTS OF 'file.json'
$.parseJSON(
'['
+ ' {'
+ ' "type": "firsttype",'
+ ' "text": {'
+ ' "id": "content"'
+ ' }'
+ ' },'
+ ' {'
+ ' "type": "secondtype",'
+ ' "text": {'
+ ' "id": "other"'
+ ' }'
+ ' }'
+ ']'
)
);
doJsonThing();
/* END UNIT TEST */
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

Access JSON kind of data from a div?

So I have a div which displays the results from Microsoft emotion API such as:
anger: 0.28446418
contempt: 0.00341128884
disgust: 0.000332433876
fear: 0.009447911
happiness: 0.02609423
neutral: 0.6288482
sadness: 0.00180563633
surprise: 0.04559612
Here is the code for displaying the data:
.done(function(data) {
// Get face rectangle dimensions
var faceRectangle = data[0].faceRectangle;
var faceRectangleList = $('#faceRectangle');
var data1="";
// Append to DOM
for (var prop in faceRectangle) {
data1 += "<li> " + prop + ": " + faceRectangle[prop] + "</li>";
}
faceRectangleList.html(data1);
// Get emotion confidence scores
var scores = data[0].scores;
var scoresList = $('#scores');
var data2="";
// Append to DOM
for(var prop in scores) {
data2 += "<li> " + prop + ": " + scores[prop] + "</li>";
}
scoresList.html(data2);
}).fail(function(err) {
alert("Error: " + JSON.stringify(err));
});
});
}
function make_graph(){
}
Now I need to plot a line from these scores.Can you tell me how to access each separate value of these scores in another function so that I can plot them as points in my graph?
Just call make_graph from inside the done callback, passing it the data, like this.
.done(function(data) {
// ... do what you already do
make_graph(data);
}).fail(function (err) {
alert("Error: " + JSON.stringify(err));
});
function make_graph(data) {
for (d in data) {
// do stuff with data
}
}
From the given information, you can make a function like this
function drawLine(dataScores) {
for (var d in dataScores) {
// dataScores[d] will give you the score
// rest of the relevent code
}
}
and call it like this
drawLine(scores);
I think it is not good practice to write data in HTML DOM, then read it back.
But if You need this, try something like this
plotData = []
var scoresList = $('#scores')
scoresList.children('li').each(function(i) {
data = i.thml()
plotData.push(i.split(':').map(i => [i[0].trim(), parseFloat(i[1])]))
});
// here You have all data in plotData
// [['anger', 0.28446418], ...]
// Draw Your plotData here
If you want to read it back from div li, here is a jquery function to covert html to jquery data object
$(document).ready(function(){
var dataArr={};
$("#scoresli" ).each(function( index ){
data=$( this ).text().split(":");
dataArr[data[0]]=data[1]
});
alert(JSON.stringify(dataArr));
});
//{"anger":" 0.28446418\n","contempt":" 0.00341128884"}

Unable to return value from node js module

exports.getDefiniton = function (text) {
var definition = "";
wn.definitions(text, {
useCanonical: true
, includeRelated: true
, limit: 3
}, function (e, defs) {
definition = defs[0].word + ': 1.' + defs[0].text;
definition += '\n2.' + defs[1].text;
definition += '\n3.' + defs[2].text;
console.log(definition)
});
return definition;
};
Console.log inside function(e, defs) works.
but the return statement doesn't seem to return the value.
How to properly return 'definition' variable?
since wn.definition is an Asynchronous call you should use promise or async/await or callback features.
Using callback your code would be like something like this (for example lets say you store this in a def.js file):
exports.getDefiniton = function (text, callback) {
var definition = "";
wn.definitions(text, {
useCanonical: true
, includeRelated: true
, limit: 3
}, function (e, defs) {
definition = defs[0].word + ': 1.' + defs[0].text;
definition += '\n2.' + defs[1].text;
definition += '\n3.' + defs[2].text;
console.log(definition);
callback(definition);
});
};
and you can use def.js module like this:
var defModule = require("./def");
defModule.getDefiniton("Hello", function (defintion) {
console.log(defintion);
});
UPDATE:
#Xuva in that case check the code below:
var defModule = require("./def");
defModule.getDefiniton("Hello", function (definition) {
displayMessage(text, definition);
//rest of the code ...
});

Trouble getting getJSON to display values from array

I am having trouble displaying the contents of a $.getJSON call. I have this code which retrieves some JSON data from a page.
var loadItems = function() {
if (hasNextPage === false) {
return false
}
pageNum = pageNum + 1;
var url = baseUrl + "json/" + pageNum + '/';
var jqxhr = $.getJSON(url, function (data) {
var a = [];
$.each(data.itemList, function (item) {
a.push("<li>" + item.title + "</li>");
});
$("<ul/>", {html: a.join(" ")}).appendTo("#anchor");
});
jqxhr.complete(function () { alert('done!'); $(window).bind('scroll', loadOnScroll); });
};
The idea is just to load a page dynamically based on scroll position, and get the JSON contents of that page (for an infinite scroll effect). The JSON structure is something like this:
{ "key1": val1, "key2": val2, "itemList": [ {"title": "title", "author": "author", "id": 100, "slug": slug, }, ... ] }
The important values are in itemList, where I have to retrieve data that will get plugged into my django template, which in turn will get some data from the view. Everything seems to work just fine, except that I can't access the data in itemList. Nothing seems to get pushed into the array. because nothing gets displayed on the page (namely, the <li> that should be created). This seems to be a simple implementation of a basic ajax move, but its not working.
EDIT:
I've done some bug tracking and have found that the code never executes the $.each loop. Not sure how to resolve this though.
you should be using error log to check what is going wrong:and always use index,obj format in $.each to be safe.
$.getJSON(url).success( function( data ) {
console.log(data);
$.each(data.itemList, function (index, item) {
a.push("<li>" + item.title + "</li>");
});
$("<ul/>", {html: a.join(" ")}).appendTo("#anchor");
}).error(function(error){
console.log(error);// see if you are getting in error log..
});
and your json data is also wrong it should be something like this:
{ "key1": "val1",
"key2": "val2",
"itemList":
[
{
"title": "title", "author": "author", "id": 100, "slug": "slug"
},.......
]
}
try with corrected data it will work.
you can check your data here:http://json.parser.online.fr/
I saw the problems in your code when you using
$.each() //It should using for dictionary.
So I have two solutions for you
1) You can use forEach method.
ex:
`if(data != null && data.itemList.length > 0)
{
data.itemList.forEach(function (item) {
a.push("<li>" + item.title + "</li>");
});
}`
==> This way will work for IE 10+, and the other browers.
2) Using the $.each method
ex:
if(data != null && data.itemList.length > 0)
{
$.each(data.itemList, function (key, item) {
a.push("<li>" + item.title + "</li>");
});
}
==> Your Json data is dictionary type, so if you use $.each you need to define two variable with
key: is the key of data.
item: is the value of data.
Note: this way will be worked for all browers version.
Hope this can helps you.
It's because your 'item' is an index, not an 'object' as you want. you have to use the second parameter:
$.each(data.itemList, function (index, item) {
a.push("<li>" + item.title + "</li>");
});
also, where is hasNextPage defined? is it defined? you might also shorten that to:
if (!hasNextPage) return;

jQuery almost winning me - UI Autocomplete " data undefined"

jQuery is almost winning me. I already saw lots and lots in StackOverflow answers and I can't find a solution to my problem.
I'm using jQuery UI Autocomplete in my project, and I need render a default message if no results is returned. I'm using "_renderItem", as you can see bellow.
var autocompleteDir = $("#search");
autocompleteDir.autocomplete({
source: function (request, response) {
populate(request.term, response);
result = $.ui.autocomplete.filter(result, request.term)
var item = {};
item.type = 'loading';
item.label = "Loading..";
item.value = "";
result.unshift(item)
response(result.slice(0, 10));
}
});
autocompleteDir.data("ui-autocomplete")._renderItem = function (ul, item) {
if (item.type === "loading") {
return $('<li></li>')
.data("ui-autocomplete-item", item)
.append("<div style='text-align: center;'>" + item.label + "</div>")
.appendTo(ul);
}
};
And here's what I'm getting on the console:
Uncaught TypeError: Cannot read property 'type' of null
Uncaught TypeError: Cannot call method 'data' of undefined
I'm using jQuery 1.10.2 and jQuery UI 1.10.3. Someone have a good idea?
Edit 1:
If I use this before "source":
response: function(event, ui){
if (ui.item.type === "loading"){
ui.content.push({label:"Loading",value:"Loading"})
}
}
I have the following error:
Cannot read property 'type' of undefined
If you help me solve this problem, can I use "response" to format and style my "loading" and my "none" result?
Edit 2
var populate = function(term, response) {
$.getJSON(
'<%= my_rails_path %>.json',
{search: term},
function(json) {
var result = [];
$.each(json, function(key, value) {
var item = {};
item.type = '';
item.label = value.name;
item.value = value.name;
result.push(item);
})
if (result.length == 0) {
var item = {};
item.type = "noResult";
item.label = "Create '" + term + "'";
item.value = term;
result.push(item)
}
response(result);
}
);
};
Edit 3
Now the problem is solved, but the label is literally showed, but I want that be rendered. See the code, you'll understand:
response: function(event, ui){
for(var i in ui.content){
if (ui.content[i].type==="loading"){
ui.content[i] = {
label:"<div style='text-align: center;'>Loading</div>",
value:""
}
}
}
}
Instead render "Loading" in the middle of the ui, all the string is showed to user (Loading).
You don't need to use _renderItem because jQuery provides an event that fires after the search returns and before the results are displayed, which you can modify to return a default value if the search return was empty. See the response event. You can do this instead:
autocompleteDir.autocomplete({
source: function (request, response) {
populate(request.term, response);
result = $.ui.autocomplete.filter(result, request.term)
var item = {};
item.type = 'loading';
item.label = "Loading..";
item.value = "";
result.unshift(item)
response(result.slice(0, 10));
},
response: function(event, ui){
if(ui.content.length === 0){
ui.content.push({label:"default",value:"value"}); /* modify to your liking */
}
}
});
Changing to this technique should solve both issues.
Edit:
In your third edit, if you want to change the value of a div somewhere else on the page, you need to do this in the response function yourself, not by returning the value in the function.
response: function(event, ui){
if(ui.content.length === 0){
$('#id-of-div-to-change').text("Loading");
}
}

Check if value from function returned is null?

This question (or similar) seems to get asked often, but I've tried many ways to check if the value returned from a function I have, is null or not; to no avail.
My function, get's URL parameters by name:
function getURLParameter(name) {
return decodeURI(
(RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1]
);
}
However, obviously a parameter may not be there, so I need to do a check on the value returned.
Doing console.log( getURLParameter('client') ); returns null...but doing null checks does not work.
I have tried the following:
if ( getURLParameter("client") !== null ) {
alert("It's there matey!");
}
if ( getURLParameter("client") != null ) {
alert("It's there matey!");
}
if ( ! getURLParameter("client") ) {
alert("It's there matey!");
}
None of these seems to work for me.
Is there anywhere I am going wrong? I can do this either in vanilla JS, or using jQuery library.
The problem is decodeURI, which is returning the string "null" when you pass null into it. The solution is to do the null check before calling decodeURI. You can find this by just breaking the function up into its parts:
function getURLParameter(name) {
var rex = RegExp(name + '=' + '(.+?)(&|$)');
var result = rex.exec(location.search);
var rv = decodeURI(
(result||[,null])[1]
);
return rv;
}
...and walking through it in the debugger built into your browser.
Or for those who prefer console.log-style debugging:
function getURLParameter(name) {
var rex = RegExp(name + '=' + '(.+?)(&|$)');
var result = rex.exec(location.search);
console.log("typeof result = " + typeof result);
console.log("result = " + result);
var rv = decodeURI(
(result||[,null])[1]
);
console.log("typeof rv = " + typeof rv);
return rv;
}
...which for getURLParameter("foo") shows us:
typeof result = object
result = null
typeof rv = string
That's because the value your function is returning is a string ('null') and therefore it fails with your tests.
var result = getURLParameter("client");
console.log('Value: ' + result); // 'null'
console.log('Data type: ' + typeof(result)); // string
console.log(result!==null); // true
console.log(result!=null); // true
console.log(!result); // false
It returns a string because you are passing the null value as parameter to the decodeURI function, which converts to the string 'null'.
So, I edited your function getURLParameter to check if the value of the param is null before calling decodeURI. See below:
function getURLParameter(name) {
var param = RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || null;
return param===null ? null : decodeURI(param[1]);
}
Now, let's run the tests again:
var result = getURLParameter("client");
console.log('Value: ' + result); // null
console.log('Data type: ' + typeof(result)); // object
console.log(result!==null); // false
console.log(result!=null); // false
console.log(!result); // true
try
if ( getURLParameter("client") != 'null' ) {
alert("It's there matey!");
}

Categories

Resources