getjson obj name and iterate child obj's - javascript

I'm fresh to js and web dev for that matter and am looking for some help.
I'm trying to grab enough information to format and make another request. This is what i have so far
JavaScript
$( document ).ready(function(sb_shows) {
$.getJSON( "http://<myname>/api/<apikey>/?cmd=shows&callback=?", function(shows_obj) {
//$.each(shows_obj, function(key, value) {
if ( shows_obj.result == "success") {
document.write(shows_obj.result);
$.each(shows_obj.data, function(key, value) {
$.each(this.cache function(key, value) {
document.write( "<p> "+value.banner+"<p>");
});
document.write( "<p>"+value.show_name+" - "+value.tvrage_id+ "<p>");
});
}
else {
document.write("fail..");
}
});
});
JSON Sample
{
"data": {
"72023": {
"air_by_date": 0,
"cache": {
"banner": 1,
"poster": 1
},
"language": "en",
"network": "HBO",
"next_ep_airdate": "",
"paused": 0,
"quality": "SD",
"show_name": "Deadwood",
"status": "Ended",
"tvrage_id": 3267,
"tvrage_name": "Deadwood"
},
"72231": {
"air_by_date": 1,
"cache": {
"banner": 1,
"poster": 1
},
"language": "en",
"network": "HBO",
"next_ep_airdate": "2013-09-13",
"paused": 0,
"quality": "HD720p",
"show_name": "Real Time with Bill Maher",
"status": "Continuing",
"tvrage_id": 4950,
"tvrage_name": "Real Time With Bill Maher"
},
},
"message": "",
"result": "success"
}
What i'm hoping to achieve is grab the id under data (e.g. 72023 and 72231), I originally thought i'd be able to do something like
$(this).parent()
Its a object however and that doesn't appear to work
Also I'd like to be able to iterate through the sub obj's, something like below
$.each(shows_obj.data, function(key, value) {
$.each($(this).cache function(key, value) {
document.write( "<p> "+value.banner+"<p>");
});
document.write( "<p>"+value.show_name+" - "+value.cache.banner+ "<p>");
});
Any and all recomendations/suggestions will be appreciated. Please explain suggestions, i'm kinda slow:)

In your first $.each() call on the data object, your key is actually the ID number you'r e looking for. Google quickly gave me an introduction to JSON which might help you out.
As per the jQuery.each documentation the this keyword is the same as the value keyword. Your $.each($(this).cache, ...) should simply be $.each(this.cache, ...) or $.each(value.cache, ...) as you're not traversing the dom ($(..)) but passing an object to the $.each() function. Just like in the earlier case if you traverse this.cache you would have your callback be called twice:
// first
key => banner
value => 1
// second
key => poster
value => 1
If you would've just wanted the banner from the cache the thing to do would've been: value.cache.banner in your original $.each(shows_obj.data, ...)

Related

json api and result with error: Cannot read property 'length' of undefined

i keep getting something i can't find out how to solve. When i run the code it tells me "Uncaught TypeError: Cannot read property 'length' of undefined". With a lot of search and reading i did not find the answer it mentions that i need to use the length of the value with a for command but i tried several solutions none of them solved the problem, this is the code:
function Cast() {
$.ajax({
type: "Get",
url: "http://www.myapifilms.com/imdb/idIMDB?idIMDB=tt2193418&token=<TOKEN>&format=json&callback=?&actors=2",
dataType: "json",
success: function (Result)
{
$.each(Result.actors, function (i, item) {
$('.div').append('<tr><td>' + Result.actors[i].actorName + '</td></tr>');
});
},
error: function () {
console.log("Error, Something went wrong!");
}
});
}
the response i get from postman:
{
"data": {
"movies": [
{
"title": "Hammer of the Gods",
"simplePlot": "A young man transforms into a brutal warrior as he travels the unforgiving landscape in search of his long lost brother, Hakan the Ferrocious, whose people are relying on him to restore order to their kingdom.",
"actors": [
{
"actorName": "Charlie Bewley",
},
{
"actorName": "Clive Standen",
etc.
From what I can see, you are expecting the "actors" array to be a direct property of "data" (i.e. your Result variable). But then the example data you've provided shows that there's a "moveies" array in between the two. Hence the error - internally the .each function will be trying to work out the length of Result.actors...but Result.actors doesn't exist, hence it's saying that it's undefined.
You've got an array of movies, so you need to loop through those first, and then loop through the actors within them.
I've created a worked example here using the data you gave, and the processing code I used. All that's missing is the Ajax bit, I've just put the data directly into a variable instead, but that shouldn't matter.
$(function() {
var Result = {
"data":
{
"movies":
[
{
"title": "Hammer of the Gods",
"simplePlot": "A young man transforms into a brutal warrior as he travels the unforgiving landscape in search of his long lost brother, Hakan the Ferrocious, whose people are relying on him to restore order to their kingdom.",
"actors":
[
{
"actorName": "Charlie Bewley",
},
{
"actorName": "Clive Standen",
}
]
}
]
}
};
$.each(Result.data.movies, function (i, movie) {
$.each(movie.actors, function (j, actor) {
$('.div').append('<tr><td>' + actor.actorName + '</td></tr>');
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="div">
</table>

How to access object field in server array response?

I made a parse.com get request, the returned data is stored in:
$scope.tastes = data.results
{
"createdAt": "2016-03-16T07:39:15.745Z",
"objectId": "Cmg8GdOv2Z",
"updatedAt": "2016-03-16T07:39:15.745Z",
"user": {
"__type": "Pointer",
"className": "_User",
"objectId": "vYOsndWlto"
},
"userTastes": [
{
"actualite": {
"checked": true
},
"economie": {
"checked": true
},
"entrepreneuriat": {
"checked": false
}
}
]
}
Well, I want to get userTastes array.
I've tried
.success(function (data, status) {
$scope.tastes = data.results.userTastes;
console.log($scope.tastes);
})
However nothing is returned. I think that I'm missing something.
My question : How do I get userTastes in $scope.tastes ?
I writing a separate answer because I believe this needs further explanation and not just the fix to your problem.
You only provided an object response in your question, but apparently you are getting an array response from your server, while you can directly access object fields, on array objects you need to access the position first, Ex:
$scope.objectResponse = {"foo":"bar"};
console.log($scope.objectResponse.foo); // Will print "bar"
in contrast array responses:
$scope.arrayResponse = [{"foo":"bar"}];
console.log($scope.arrayResponse[0].foo); // Will print "bar"
Just make sure you are getting the response you want from your server.
results[0].userTastes work worked perfectly thanks !
If someone have a tutorial link or good course about array and objects in JS because I'm a little bit confused about that.
Have a good day !

Most efficient way to search large multi nested JSON dataset?

What is the most efficient way to search a large dataset of JSON (Up to 27k entries) using jQuery or JS. I'd like to create a search field that runs the function on keyup.
So if a users starts typing it returns all values that match the title, along with the link for that title.
My JSON is nested alphabetically A-Z. Example below
"Numbers": [
{
"Title": "20th Century Period Pieces",
"Link": "#"
},
{
"Title": "20th Century",
"Link": "#"
}
],
"A": [
{
"Title": "Action Comedies",
"Link": "#"
},
{
"Title": "Action",
"Link": "#"
}
],
"B": [
{
"Title": "British",
"Link": "#"
},
{
"Title": "Borderline",
"Link": "#"
}
],
// And so forth
I'm aware this may cause the browser to freeze up searching such a large list on every keyup so I might have to revert to a search button and loading animation.
Any help is much appreciated.
You could implement a Tree-Structure which would allow you to search more efficient, tho I do not think this is what you want to do. You could also have a backend handle the search and give the results, this is what I would do. So have PHP process the search query and return a new JSON set. Your last option is to just let the browser freeze.
I would prefer the PHP-solution. The loading animation can just wait until PHP returned the data and then show it. This uses the server to process the request, so nothing will freeze - it's a really good solution in my opinion.
For the user, I'd suggest the following: Register keystrokes and give it a "timeout". As soon as the user didn't type for 500ms, query the PHP-script. You can do this via XMLHttpRequest and parse the result using JSON.parse. No need for jQuery here at all.
Personally I think you should send all that data to the client anyway, if you create an API for the data and filter it via an AJAX request, which returns the filtered result. Looking at your example data, it seems that this isn't user specific, so pulling the data from the database / wherever and caching it seems to be the best solution.
jQueryUI Autocomplete has an example on how to achieve this here
This is their example, making an AJAX request whenever the user has typed in a minimum of 3 characters:
$( "#city" ).autocomplete({
source: function( request, response ) {
$.ajax({
url: "http://gd.geobytes.com/AutoCompleteCity",
dataType: "jsonp",
data: {
q: request.term
},
success: function( data ) {
response( data );
}
});
},
minLength: 3,
select: function( event, ui ) {
log( ui.item ?
"Selected: " + ui.item.label :
"Nothing selected, input was " + this.value);
},
open: function() {
$( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );
},
close: function() {
$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
}
});
You don't have to use jQuery Autocomplete, but you get the idea.
Create a map with a structure that is easier to search
var map = [];
(function _search(o) {
if ( typeof o !== 'object' ) return;
if (Array.isArray(o)) {
o.forEach(function(item) { _search(item) });
} else if ('Title' in o) {
map.push(o);
} else {
for (var key in o) {
if ( typeof o[key] === 'object' ) {
if ( Array.isArray(o[key]) ) {
o[key].forEach(function(item) { _search(item) });
} else {
_search(o[key]);
}
}
}
}
}(obj));
Then you can just filter the map
var result = map.filter(function(obj) {
return obj.Title.toLowerCase().indexOf(SearchValue.toLowerCase()) === 0;
});
FIDDLE

JSON file read with Select2 plugin

I have this JSON file (truncated):
{
"** Please contact a mod by posting on the forums **": {
"tvdb_id": "257937",
"last_updated": 1341780286,
"images": {
"poster": "http://zapp.trakt.us/images/posters/17288.jpg",
"fanart": "http://zapp.trakt.us/images/fanart/17288.jpg"
}
},
"Jamies Best Ever Christmas": {
"tvdb_id": "214161",
"last_updated": 1329701153,
"images": {
"poster": "http://zapp.trakt.us/images/posters/9126.jpg",
"fanart": "http://zapp.trakt.us/images/fanart/9126.jpg"
}
},
"Kuromajo-san ga Tooru!!": {
"tvdb_id": "257914",
"last_updated": 1395775431,
"images": {
"poster": "http://zapp.trakt.us/images/posters/15640.jpg",
"fanart": "http://zapp.trakt.us/images/fanart/15640.jpg"
}
}
}
and this code:
$(".tvshow").select2({
placeholder: "Type in a TV show",
multiple: true,
maximumSelectionSize: 1,
minimumInputLength: 2,
maximumInputLength: 20,
query: function(query) {
var data = {results: []};
var value = $(".select2-input").val();
$.ajax ({
type: "GET",
url: 'shows.json',
dataType: "jsonp",
json: "callbackname",
crossDomain : true,
success: function (result) {
$.each(result, function (i, show) {
// write everything in an array
data.results.push({id: this.tvdb_id, text: this.title, poster: this.images.poster, bg: this.fanart });
console.log(this.title);
selectedTVshow = this.title;
// results = data.results;
// return array
query.callback(data);
})
},
error: function (data) {
// console.log('error');
}
})
}
})
When I search for Jamies Best Ever Christmas I want it to return the name and the respective tvdb_id and details.
In my above code you can see console.log(this.title);. This is wrong, because there is no title: value inside the JSON object. How can I get this to work and retrieve data?
Demo: http://jsfiddle.net/6pGFC/ (use first input field to get autocomplete, not second)
Thanks a lot!
There are a couple of issues in your example.
You dropbox file is not JSONP. It is JSON. (so jquery cannot parse it)
Since you are iterating over an object and not over an array with $.each the arguments passed to the function are key and value, so in your case the i is the key. (see https://api.jquery.com/jQuery.each/)
In general the searching should happen in the server side, and not on the client (especially for 5mb files)
Here is demo that has fixed (i have re-uploaded the corrected file to my dropbox) the above two problems http://jsfiddle.net/6pGFC/3/
(too slow though since it tries to display all the json)
It looks like you want to do a mapping operation to transform the data into something easier to work with. Underscore.js is a fantastic tool for this.
var mappedResults = _.map(result, function(key, value, list) {
return {name:key, tvdb_id:value.tvdb_id};
});
This will turn this:
{
"** Please contact a mod by posting on the forums **": {
"tvdb_id": "257937",
"last_updated": 1341780286,
"images": {
"poster": "http://zapp.trakt.us/images/posters/17288.jpg",
"fanart": "http://zapp.trakt.us/images/fanart/17288.jpg"
}
},
"Jamies Best Ever Christmas": {
"tvdb_id": "214161",
"last_updated": 1329701153,
"images": {
"poster": "http://zapp.trakt.us/images/posters/9126.jpg",
"fanart": "http://zapp.trakt.us/images/fanart/9126.jpg"
}
},
"Kuromajo-san ga Tooru!!": {
"tvdb_id": "257914",
"last_updated": 1395775431,
"images": {
"poster": "http://zapp.trakt.us/images/posters/15640.jpg",
"fanart": "http://zapp.trakt.us/images/fanart/15640.jpg"
}
}
}
...into this:
[
{name:"** Please contact a mod by posting on the forums **", tvdb_id:"257937"},
{name:"Jamies Best Christmas", tvdb_id:"21461"},
{name:"Kuromajo-san ga Tooru!!", tvdb_id:"257914"}
]
Once that's done, you can filter it like so:
function getMatchingIds(searchterm) {
return _.filter(mappedResults, function(entry) {
return (entry.name.indexOf(searchterm) != -1);
}
}
I should also add that jQuery has its own $.map feature that does something similar.

Javascript objects - feature missing from overall object, yet exists

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);

Categories