Most efficient way to search large multi nested JSON dataset? - javascript

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

Related

Simple get API call in jQuery

I have a url to which I need to make a get call. The url returns the following JSON response when run in POSTMAN.
{
"status": true,
"data": [
{
"id": 1,
"suggestion": "shirt"
},
{
"id": 2,
"suggestion": "jeans"
},
{
"id": 3,
"suggestion": "puma"
},
{
"id": 4,
"suggestion": "spykar"
}
],
"lastPageNumber": 0
}
I need to get the "suggestion" key's value and append to my list.
Following is my code to get the data and append its values to a list as shown.
jQuery.getJSON( url, function( response ) {
$.each(response.data ,function() {
$.each(this, function (key, value) {
if(key=="suggestion") {
$('#suggestions').append('<li class="suggestItem">'
+ '<a>' + value + '</a>' + '</li>'});
}
);
});
});
And this is the ul to which I have to append these list items.
<ul id="suggestions" style="position: absolute; list-style-type: none;">
However, I do not seem to get any data in response or maybe I am not traversing through the JSON array correctly. So, I need help in whether I have written the right code or not.
Also additional information:
I am running this project on localhost and the url is something http://example.anotherExample.net so does this also affect (I read something about cross domain in ajax).
You can try something like following
$.each(obj.data, function(){
$('#suggestions').append('<li class="suggestItem">' + '<a>' + this.suggestion + '</a>' + '</li>');
});

select2: load asynchronous data through promise

What is the best place to load the options for select2 asynchronously. I want the same facility as ajax, but instead of select2 sending an ajax request, it needs to load the values asynchronously from a promise object. Below code works, in which I load the data in query, but which means every keystroke, invocation of select dropdown, it will query the data. so, what is the correct configuration?
code:
var items2 = [
{
"Id": 1,
"Name": "First"
},
{
"Id": 2,
"Name": "Second"
},
{
"Id": 3,
"Name": "Third"
}
];
var names = function () {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve(items2);
}, 200);
return deferred.promise;
};
var query: function (query) {
var results = [];
names().then(function(d){
$.each(d, function(index, item){
results.push({
id: item.Id,
text: item.Name
});
});
query.callback({ results: results });
})
};
Edit
looking at the source, it looks like it only allows either ajax or local for querying data. It would have been ideal if local takes a function which returns the data. Am I on the right track? is there an easy way to patch it?
thanks
// exports
window.Select2 = {
query: {
ajax: ajax,
local: local,
tags: tags
}, util: {
debounce: debounce,
markMatch: markMatch,
escapeMarkup: defaultEscapeMarkup,
stripDiacritics: stripDiacritics
}, "class": {
"abstract": AbstractSelect2,
"single": SingleSelect2,
"multi": MultiSelect2
}
};
Edit2:
'local` indeed accepts a function. but it doesn't play nicely with remote data, as the data is received with a delay (async), drop down is not populated with the new data. I have to close and open the drop-down again. This is not intuitive for the user.
As far I see, select2 invokes an ajax call for each key stroke, and opening the select box. I can get the same behavior using query (with promise) as in the original question. I was expecting select2 loads the data once, then do rest locally (search, and further invocation till any data change). looks like this is not an option. I may just cache my results locally. Better answer welcome.

How can I autoselect the first option in Twitter typeahead.js

Please note that this is for Twitter typeahead.js which is not the same as boostrap typeahead (which has been removed from Bootstrap in 3.0)
According to this issue on Github the feature has been added, but I can not see how to implement it.
I have tried
autoselect: 'first'
and
autoselect: true
Neither seems to work.
Make sure you're using the latest release (>v0.10.0). This feature was merged into the master branch on Feb 2, 2014. Here's how you would initialize a typeahead with the autoselect: true option:
$('.typeahead').typeahead({
autoselect: true
},
{
name: 'my-dataset',
source: mySource
});
For more, check out the documentation on initializing the typeahead and the autoselect option.
There is an autoSelect option that about to be merged to master. You can clone the requester's repo and use it immediately (working great for me).
https://github.com/twitter/typeahead.js/pull/1356
https://github.com/hongz1/typeahead.js
I honestly couldn't make autoselect option work, besides I needed to implement it in a way that it autoselects the first option ONLY IF I HAD ONE RESULT from the source.
This worked for me:
...
success: function(data) {
process(data);
if (data[0] != null) {
$(".tt-dropdown-menu .tt-suggestion").trigger("click"); //autoselect the first element.
}
}
...
I used a dirty trick, I just added empty element as First Item, works like a charm...
var typeaheadSource = [{ id: 0, firstName: " | | "}, <?php print $product_list; ?>];
Which look like this...
var typeaheadSource = [{ id: 0, firstName: " | | "}, { id: 1, firstName: "Coca Cola 1.5L | 9555589200415 | 0.00"}];
In my case I'm populating the data in PHP.
You may wanna do some checking in the onSelect event, if it requires in your case.
Hope this helps.
Tested on version 0.11.1 for ajax requests to autoselect the first option, only when 1 result is returned
The code hooks the return from the ajax request, if the return is only 1 item it will tell the typeahead the request failed ( preventing the list from opening ) and manually set the result
const $field = $( '#auto_complete' );
const bloodhound = new Bloodhound( {
datumTokenizer: Bloodhound.tokenizers.whitespace,
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: `remoteUrl?&q=%QUERY`, // Set the proper URL...
wildcard: '%QUERY',
rateLimitWait: 500,
cache: false,
transport: ( settings, onSuccess, onError ) =>
{
return $.ajax( settings )
.done( ( data, textStatus, jqXHR ) =>
{
if ( data.length === 1 )
{
onError( jqXHR, 'abort', '' ); // Pretend the request failed
$field.val( data[0].code ) // Set the value in the input, don't use $field.typeahead('val', results[0].code);
$field.blur();
$field.trigger( 'typeahead:select', [ data[0] ] ); // Simulate the user selecting the option
return;
}
onSuccess( data, textStatus, jqXHR );
} )
.fail( onError );
}
}
} );
$field.typeahead(
{ /* .. options...*/ },
{
source: bloodhound,
}
);

getjson obj name and iterate child obj's

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, ...)

The confuse about use jquery $.ajax to read javascript file

suppose I have a config javascript file:
window.Config = {};
Config.UI = {
"Area": {},
"Layer": {},
"Sprites": {},
"Audio": {}
};
Config.UI.Area = {
"prop": {
"uuid": {
"_type": "string",
},
"frame": {
"_type": "rect",
"_value": {
"x": "0",
},
"_aka": "frame"
},
"zIndex": {
"_type": "string",
}
},
then I want use $.ajax to read this file:
$.ajax({
url:'js/config.js',
success:function (data, textStatus) {
console.log(data);
}
})
the question is how can I get some key's value in the config,by use the data $.ajax return?
like the "Config.UI" or the 'uuid' in ui.area.prop?Or can I convert them to json?
Rather than use AJAX, why not just insert a script?
var script = $('<script>');
script.attr('type', 'text/javascript');
script.attr('src', 'js/config.js');
script.bind('load', function() {
// use window.Config
});
script.appendTo('head');
icktoofay has a good suggestion, and the issue with the jQuery.ajax call looks to be a missing dataType: 'script' option which will evaluate the response and should give you object access. You might want to look into jQuery.getscript() as well.
I find it very useful and powerful to store data on the server as javascript objects and read them using Ajax. And it is very easy to do. Let me give you an example from an educational application I have written.
This is an example table of contents file (l1contents.js) that I would store on the server:
{
title : "Lesson 1",
topics : [
{name : "Topic 1", file : "l1t1data.js" },
{name : "Topic 2", file : "l1t2data.js" },
]
}
This is the javascript code I use to process the file:
$.ajax({
url : contentsFileName, // would be set to 'l1contents.js'
dataType : 'text', // yes this is correct, I want jquery to think this is text
cache : false,
success: function(data) {
var contentsObj = eval('(' + data + ')');
var lessonTitle = contentsObj.title;
for (var i = 0; i < contentsObj.topics.length; i++) {
var topic = contentsObj.topics [i];
// process topic.name and topic.file here
}
}
});
Obviously, this is simplified, but hopefully you get the idea. I simply use eval to set the object. And note that I don't even need any javascript code defining the structure of contentsObj. (I, of course, do have extensive comments defining the structure of my objects, but they are simply comments, not code.)
if your json file contains json data than you can use parseJSON() , toJSON() method.
and another solution is use eval(), this conver json data to javascript object so you can easly get a value by giving key.

Categories