I am using an approach described in detail at Dictionary Lookups in Javascript (see the section"A Client-Side Solution") to create an object that contains a property for each word in the scrabble dictionary.
var dict = {};
//ajax call to read dictionary.txt file
$.get("dictionary.txt", parseResults);
function parseResults(txt) {
var words = txt.split( "\n");
for (var i=0; i < words.length; i++){
dict[ words[i] ] = true;
}
console.log(dict.AAH);
console.log(dict);
if (dict.AAH == true) {
console.log('dict.AAH is true!');
}
}
(updated code to use an earlier answer from Phil)
I can't figure out why dict.AAH is returning undefined, but the dict object looks fine in the console. Screenshots from Firebug below.
Console:
Drilled down into "Object { }"
How can I check a given word ("AAH", in this case) and have it return true if it is a property in the dict object defined as true?
Live example
Code on Github
The problem isn't your code. You have invisible characters in your words, which you fail to clean up.
You can verify this by using this as your results parser
function parseResults(txt) {
// clean the words when we split the txt
var words = txt.split("\n")
.map($.trim)
.splice(0,3); // Keep only 3 first ones
if(btoa(words[2]) !== btoa('AAH')){ // Compare in Base64
console.log('YOU HAVE HIDDEN CHARS!');
}
}
And you can fix it by whitelisting your characters.
function parseResults(txt) {
// clean the words when we split the txt
var words = txt.split("\n").map(function(el){
return el.match(/[a-zA-Z0-9]/g).join('');
});
for (var i=0; i < words.length; i++){
dict[ words[i] ] = true;
}
console.log(dict.AAH);
console.log(dict);
if (dict.AAH == true) {
console.log('dict.AAH is true!');
}
}
I would recommend cleaning it up on the server side since running regex on every element in an array as large as seen in your live site might cause performance issues.
It's probably a race condition. You're loading the dictionary in a GET and then immediately (while the request is being made) those console.log commands are being called (and the one comes back undefined). Then the data is actually loaded by the time you debug. Everything should be done in a callback or deferred. It's an understandable quirk of debuggers that's caught me up before.
Get ajax requests are asynchronous. This means that while the whole operation that occurs in the ajax request is going, javascript keeps reading the next lines.
The problem then is you are logging values that the ajax request did not manage to retrieve early enough.
To get around the issue you can include the log calls inside your ajax request callback as below
var dict = {};
//ajax call to read dictionary.txt file
$.get("dictionary.txt", function( txt ){
var words = txt.split( "\n");
for (var i=0; i < words.length; i++){
dict[ words[i] ] = true;
}
//Now inside these console.log will run once you DO have the data
console.log(dict.AAH);
console.log(dict);
});
//Stuff out here will run whether or not asynchronous request has finished
I WOULD RECOMMEND USING THE WHEN METHOD IN JQUERY FOR THIS TYPE OF SCENARIOS EVEN MORE AS THE BEST SOLUTION
HERE IS HOW WHAT I THINK WOULD BE MOST PROPER FOR COMPLEX PROJECTS
var dict = {};
//ajax call to read dictionary.txt file
function getDictionary(){
return $.ajax("dictionary.txt");
}
/*I recommend this technique because this will allow you to easily extend your
code to maybe way for more than one ajax request in the future. You can stack
as many asynchronous operations as you want inside the when statement*/
$.when(getDictionary()).then(function(txt){//Added txt here...forgot callback param before
var words = txt.split( "\n");
for (var i=0; i < words.length; i++){
dict[ words[i] ] = true;
}
//Now inside these console.log will run once you DO have the data
console.log(dict.AAH);
console.log(dict);
});
You're trying to output dict before it has been populated by the $.get success handler.
Try this:
// If the browser doesn't have String.trim() available, add it...
if (!String.prototype.trim) {
String.prototype.trim=function(){return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');};
String.prototype.ltrim=function(){return this.replace(/^\s+/,'');};
String.prototype.rtrim=function(){return this.replace(/\s+$/,'');};
String.prototype.fulltrim=function(){return this.replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,'').replace(/\s+/g,' ');};
}
/**
* Parses the response returned by the AJAX call
*
* Response parsing logic must be executed only after the
* response has been received. To do so, we have to encapsulate
* it in a function and use it as a onSuccess callback when we
* place our AJAX call.
**/
function parseResults(txt) {
// clean the words when we split the txt
var words = txt.split("\n").map($.trim);
for (var i=0; i < words.length; i++){
dict[ words[i] ] = true;
}
console.log(dict.AAH);
console.log(dict);
if (dict.AAH == true) {
console.log('dict.AAH is true!');
}
}
// global object containing retrieved words.
var dict = {};
//ajax call to read dictionary.txt file
$.get("dictionary.txt", parseResults);
As another user commented, jQuery's $.when lets you chain such code.
By the way, if all you want to do is know if a word is in the results you can do:
function parseResults(txt) {
// clean the words when we split the txt
var words = txt.split("\n").map($.trim);
if ($.inArray('AAH', words)) {
console.log('AAH is in the result set');
}
}
I think the problem lays in that you have dict defined as an object but use it as an array.
Replace var dict = {} by var dict = new Array() and your code should work (tried with your live example on Google Chrome).
Related
First off, know that I am very new noob. The code below works except for the "if" portion of the "if-else" portion. The "searchTerm" that is alerted is the last value that a user enters into an input field with multiple values. So if user enters "a, b, c, d", and "a" (or any other value) meet the ===0 criteria, "d" is what is alerted.
I have researched here in stackoverflow and googled other areas and learning about callbacks and promises and that .getJSON is asynchronous and visibility outside of JSON and such but I have to admit I'm obviously not getting it and I'm sure I'm missing something simple. Any insight/help will be greatly appreciated.
for (var l = 0; l < searchTermArray.length; l++) {
searchTerm = searchTermArray[l];
searchURL = buildURL(searchTerm);
getResults(searchURL, searchTerm);
function getResults() {
$.getJSON(searchURL, function (responses) {
presentResults(responses, searchTerm);
});
}
function presentResults(responses, searchTerm) {
response = responses.search;
if (responses.search.return.count === 0) {
alert(searchTerm + " Not Found");
} else {
alert("Results found");
****Do other stuff with the results****
}
}
}
Your issue is that for loop is not waiting for your asynchronous getjson. Searchurl and search term is overridden by every iteration. That is why you always get the last search term. Write a function and pass all the search urls and callback from it.
I hope this will lead you to the solution.
PS: Your function definitions are also inside the for loop which is not preferred.
I went ahead and separated the functions away from the for loop and then wrapped the .getJSON in a function of its own and passed the searchURL and searchTerm parameters which allowed me to works with the searchTerm for which the criteria was met.
function getResults(searchURL, searchTerm) {
responses = '';
$.getJSON(searchURL, function (responses) {
presentResults(responses, searchTerm);
}); // close JSON
}
I need to scan the trip array and calculate the travel time between the current trip with each trip in the array and select the shortest one. For calculation i need to send google maps api call.
I am very confused about the asynchronous callback function .
Can anyone help me on this how to send api call within for loop and check the results and continue?
Thank you.
The trips are in my array list;
Array :
array=[trip1,trip2, trip3,....];
JS :
function assigntrips(array){
var triplist = [];
for(var i=0; i< array.length; i++){
var fstnode = array[i];
for(var j=i+1; j<array.length; j++){
//here i want to get the response from google api and decide if i want to choose the trip.
if not the for loop continues and send another api call.
}
}
}
function apicall(inputi, cb){
var destination_lat = 40.689648;
var destination_long = -73.981440;
var origin_lat = array[inputi].des_lat;
var origin_long = array[inputi].des_long;
var departure_time = 'now';
var options = {
host: 'maps.googleapis.com',
path: '/maps/api/distancematrix/json?origins='+ origin_lat +','+origin_long+ '&destinations=' + office_lat + ',' + office_long + '&mode=TRANSIT&departure_time=1399399424&language=en-US&sensor=false'
}
http.get(options).on('response',function(response){
var data = '';
response.on('data',function(chunk){
data += chunk;
});
response.on('end',function(){
var json = JSON.parse(data);
console.log(json);
var ttltimereturnoffice = json.rows[0].elements[0].duration.text;
//var node = new Node(array[i],null, triptime,0,ttltimereturnoffice,false);
//tripbylvtime.push(node);
cb(ttltimereturnoffice + '\t' + inputi);
});
});
}
You cannot check the results in the loop. The loop is in the past, the callbacks happen in the future - you can't change that. There are only two things you can do, and one is an abstraction of the other:
1) You can create your callback in such a manner that it will collect the results and compare them when all are present.
2) You can use promises to do the same thing.
The #1 approach would look something like this (while modifying the cb call in your code appropriately):
var results = [];
function cb(index, ttltimereturnoffice) {
results.push([index, ttltimereturnoffice]);
if (results.length == array.length) {
// we have all the results; find the best one, display, do whatever
}
}
I don't quite know what library you are using, and if it supports promises, but if http.get returns a promise, you can do #2 by collecting the promises into an array, then using the promise library's all or when or similar to attach a callback on all gets being done.
I have the following piece of code of which I'm worried for performance wise. I'm not sure if it's a good idea to loop through $.ajax just like that. Is there a more efficient way to loop through an array in jQuery ajax?
What this code is supposed to do:
This code is supposed to take a bunch of URLs through a text area and if the URLs are broken into new lines, then each URL will be part of the urls_ary array. Otherwise, if there is not line break and the entered text area value is an URL, the value will be stored in single_url.
Now, I need to send these URLs (or URL) to my server-side script (PHP) and process those links. However, if the array urls_ary is the one to be sending data through AJAX, I'd need to send each URL individually, causing me to run the $.ajax call inside a for loop, which I think is inefficient.
var char_start = 10;
var index = 0;
var urls = $('textarea.remote-area');
var val_ary = [];
var urls_ary = [];
var single_url = '';
urls.keyup(function(){
if (urls.val().length >= char_start)
{
var has_lbrs = /\r|\n/i.test(urls.val());
if (has_lbrs) {
val_ary = urls.val().split('\n');
for (var i = 0; i < val_ary.length; i++)
{
if (!validate_url(val_ary[i]))
{
continue;
}
urls_ary[i] = val_ary[i];
}
}
else
{
if (validate_url(urls.val()))
{
single_url = urls.val();
}
}
if (urls_ary.length > 0)
{
for (var i = 0; i < urls_ary.length; i++)
{
$.ajax({
// do AJAX here.
});
}
}
else
{
$.ajax({
// do AJAX here.
});
}
}
});
function validate_url(url)
{
if(/^([a-z]([a-z]|\d|\+|-|\.)*):(\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*#)?((\[(|(v[\da-f]{1,}\.(([a-z]|\d|-|\.|_|~)|[!\$&'\(\)\*\+,;=]|:)+))\])|((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=])*)(:\d*)?)(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*|(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)?)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)){0})(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|\/|\?)*)?$/i.test(url)){
return true;
}
return false;
}
Doing the $.ajax calls in a loop isn't the inefficient part. The AJAX requests will queue up, waiting for an available connection (only a certain number of requests per connection are allowed at a time). What's inefficient is the fact that you're doing multiple AJAX calls. Ideally, you could add the ability on the server to process multiple URLs at a time, then post an array of URLs in your client code instead of doing multiple requests.
So basically, the only way to be more efficient is to change the server-side code, then rewriting the client code should be straightforward.
I'm trying to load the content of the several CSV files into a new array. CSV files have a typical structure, with a label in the first row, and values (both string and real numbers) separated by commas. This part of code is responsible for loading the data for future use with Google Maps Api (not a problem for now, since I'm stuck on just loading the data). I would like to have a structure, in which I could call an element by it's name, that's why the var nodedata = {}; is created.
So the thing I totally don't get is why some part of the code is not being executed at all? console.log(nodedata); is empty, at least not in my Firefox console.
That's my attempt to the problem - links to the csv files are in the code.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js"></script>
<script src="http://jquery-csv.googlecode.com/files/jquery.csv-0.71.js"></script>
<script type="text/javascript">
var nodes = {};
var generation = {};
var nodedata = {};
$.get('https://dl.dropboxusercontent.com/u/25575808/energy/nodes.csv', function (response) {
nodes = $.csv.toObjects(response);
console.log(nodes);
});
$.get('https://dl.dropboxusercontent.com/u/25575808/energy/generation.csv', function (response) {
generation = $.csv.toObjects(response);
console.log(generation);
});
function getGeneration (nodename){
gen = 0;
for (var i = 0; i < generation.length; i++) {
if (generation[i].datetime == "2013-01-01 01:00"){
if (generation[i].node == nodename){
gen = gen + Number(generation[i]["output (MW)"])
}
}
}
return gen;
}
for (var i = 0; i < nodes.length; i++) {
nodedata[nodes[i].Node] = {
center: new google.maps.LatLng(nodes[i].Latitude,nodes[i].Longitude),
nodegen : getGeneration(nodes[i].Node)
}
}
console.log(nodedata);
I believe the problem you're having is unrelated to the usage of CSV data, rather it is the fact that the data is being loaded asynchronously.
You are executing 2 $.get() requests to load the files, which will take some time to download the files. The browser does not wait for them to finish before continuing through the rest of the code.
Therefore, it is possible for console.log(nodedate) to be executed before any data exists inside the nodes array.
An easy way to handle this is to stack your callback functions so that the first GET request completes -> run the 2nd GET request -> finally, run the processing code.
Check out this reorganization of the code: http://jsfiddle.net/Vr7sw/
(I removed the Google Maps line since I don't have the library loaded)
the problem is, the $.get requests are asynchronous (see jquery documentation), try to call to a function, into your callback body like this :
function nodesToJson(nodes) {
for (var i = 0; i < nodes.length; i++) {
nodedata[nodes[i].Node] = {
center: new google.maps.LatLng(nodes[node].Latitude,nodes[node].Longitude),
nodegen : getGeneration(nodes[i].Node)
}
}
console.log(nodedata);
}
$.get('https://dl.dropboxusercontent.com/u/25575808/energy/nodes.csv', function (response) {
nodes = $.csv.toObjects(response);
//when the request are ready, process the nodes
nodesToJson(nodes);
});
I've got a webpage which reads an XML file and loads the contents into a div on the page. As a part of this process, I need to identify all of the namespace prefixes and corresponding URIs declared in that file. I'm using jQuery to get and load the file like this:
$.get(sourceURI, function (data) {
var nsList = getNamespaces(data);
var target = $('#my_div');
target.html(data);
});
where getNamespaces is a function taking the result of the get, and returning an object in the form:
object = {
prefix1: uri1, //e.g xmlns:foo="http://bar.com" -> { foo: "http://bar.com" }
prefix2: uri2,
....
prefixn: urin
}
I have a sinking feeling that the answer may be a regex, but obviously that requires me to write one, and suffer over-used adages about having two problems from my colleagues. Is there a better way, or if not could someone point me in the right direction in constructing a regex?
Thanks!
If your browser is XHTML-compliant, you can use its parsing facilities to iterate over the XML elements with jQuery instead of processing a raw string with regular expressions:
function getNamespaces(data)
{
var result = {};
$(data).each(function() {
recurseGetNamespaces(this, result);
});
return result;
}
function recurseGetNamespaces(element, result)
{
var attributes = element.attributes;
for (var i = 0; i < attributes.length; ++i) {
var attr = attributes[i];
if (attr.name.indexOf("xmlns:") == 0) {
var prefix = attr.name.substr(6);
if (!(prefix in result)) {
result[prefix] = attr.value;
}
}
}
$(element).children().each(function() {
recurseGetNamespaces(this, result);
});
}
You can find a fiddle demonstrating this method here. (Disclaimer: that fiddle uses JSON.stringify() to display the results, so that part of the code might not work with browsers other than Firefox).