Issues looping through a list of arrays with null data - javascript

This is my first question here, hoping you can help. Currently I am trying to loop through an API list of 100 arrays all of which contain one string of data. My loop filters through for numerical data and prints it to a div id. However when I hit data with "#N/A" instead of digits, it breaks my loop. I have tried nesting an if statement that would check if data is null or not, but as it treats null data as an object, this does not work. I have included commented out code to show the things I have tried:
var xhr = new XMLHttpRequest();
var URL = "https://spreadsheets.google.com/feeds/list/0AhySzEddwIC1dEtpWF9hQUhCWURZNEViUmpUeVgwdGc/1/public/basic?alt=json";
xhr.open("GET", URL, false);
xhr.send();
var statusResponseStringify = JSON.stringify(xhr.responseText, "", 2);
var statusResponseParse = JSON.parse(xhr.responseText);
var Find = statusResponseParse.feed.entry;
for (var i = 0; i < Find.length; i++) {
var FTSEContent = statusResponseParse.feed.entry[i].content.$t;
document.getElementById("FTSEName").innerHTML+=FTSEContent + "<br><br>";
var text = FTSEContent;
var value = text.match(/(\d[\d\.]*)/g);
//var price = value[0];
//var change = value[1];
console.log(value);
/*if (typeof value === "number") {
document.getElementById("Change").innerHTML+=value + "<br>";
}
else if (typeof value === null) {
document.getElementById("Change").innerHTML+="N/A" + "<br>";
}
else if (typeof value === "object") {
document.getElementById("Change").innerHTML+="Smell" + "<br>";
}
else {
document.getElementById("Change").innerHTML+="poo" + "<br>";
};*/
if (typeof value == "undefined") {
document.getElementById("Print").innerHTML+="N/A" + "<br>";
}
else {
document.getElementById("Print").innerHTML+=value[0] + "<br>";
};
};
This is the console I get back when I run this code
Could anyone help me with some code ideas to circumvent the null responses when looping. I would ideally like to print the numbers and print an N/A whenever there is a null or #N/A within the API data.
Thank you all!

Rewrite your check: instead of if (typeof value == "undefined") it should be...
if (value === null) { ... }
... as .match() returns null on non-matching, and not undefined.
As a sidenote, your code can be simplified a bit. First, you don't have to repeat the whole statusResponseParse.feed.entry... expression in FTSEContent, use Find instead:
var FTSEContent = Find[i].content.$t;
Second, my understanding is that you check for number in that content string. In this case, you can adjust your pattern a bit:
var value = FTSEContent.match(/(\d+(?:\.\d+)?)/);
... so it won't consume such illegal numbers as '3..' and '3.14.15' (in the last case, only 3.14 will be matched), and doesn't have to match globally (you only process the first result anyway).

Related

javascript .data() cuts the string content by whitespace

So, I have this problem where I have a backwards-button in a webapplication. This is the javascript-code for the button:
function getPrevFunction()
{
localDBSelect("prevViews", function (prevViews)
{
if (prevViews)
{
var prevViewObject = $.parseJSON(prevViews);
var prevViewArray = prevViewObject['funcObjects'];
if (prevViewArray.length > 1)
{
var prevArrayIndex = prevViewArray.length - 2;
var actArrayIndex = prevViewArray.length - 1;
var prevFuncObject = prevViewArray[prevArrayIndex];
var prevFunc = prevFuncObject['function'];
var prevConfig = prevFuncObject['config'];
var inData = prevFuncObject['inData'];
prevViewArray.splice(actArrayIndex, 1);
if (inData !== "")
{
if (prevFunc !== "getGuiSiteList")
{
inData = "<div data-param=" + JSON.stringify(inData) + ">";
}
$('#fieldcontain')[prevFunc](inData, prevConfig);
}
else {
$('#fieldcontain')[prevFunc](prevConfig);
}
if (prevViewArray.length === 1)
{
setVisibilityForBackBtn(false); //If last..
}
prevViewObject['funcObjects'] = prevViewArray;
localDBInsert("prevViews", JSON.stringify(prevViewObject));
}
else {
setVisibilityForBackBtn(false);
}
$('#subcontainer').html("");
if(!$('#fieldcontain').is(":visible"))
{
$('#fieldcontain').show();
}
}
});
}
My problem is that I don't always get the entire content of the json-object. Eg; the json, at the beginning it looks like this:
input = {site: "GAV", location: "EG", set: "INVENTORY", binnum: "B01 T09"}
but after I have tried to fetch the json that is being passed as a data/attribute with an html-element like so:
var input = $(inData).data("param");
the value I recieve looks as follows:
input = "{"site":"GAV","location":"EG","set":"INVENTORY","binnum":"B01"
As you can se it has by some reason cut off all the characters after the whitespace, despite nothing happens between the fact that the last functions is added to the list, and that function is then called again, too be able to go backwards in the application.
I do realize that my explanation is messy and probably hard to understand, but this is the best that I can explain it.
I can provide more code if necessary.
So, I do need the entire json for the getPrevFunction (it is passed as "prevViews")
Use encodeURIComponent() and decodeURIComponent() like below
Setting the data
inData = "<div data-param=" + encodeURIComponent(JSON.stringify(inData)) + ">";
Getting the data
var input = JSON.parse(decodeURIComponent($(testDv).data('param')));
Now there will be no cuttings in the object due to whitespace.

Is using new Function considered a security risk?

I have a function to help me create ad hoc objects and save me time typing.
Additional EDIT: to clarify this function will only ever sit inside an anon function.
(function(){ // clarification of the functions location
var objectPump = function (props, defaults){
var str;
if(typeof defaults === "string"){
defaults = defaults.split(",");
}else
if(typeof defaults === "undefined" || !defaults.isArray){
defaults =[];
}
if(props !== undefined){
if(typeof props === "string"){
props = props.split(",");
}
}else{
throw new TypeError("No properties defined for objectPump.");
}
// create function body
str = "var obj={};";
props.each( function(p,i) {
str += "obj." + p + "=";
if (typeof defaults[i] === "string") {
str += p + "===undefined?" + '"' + defaults[i] + '":';
} else
if (typeof defaults[i] === "number") {
str += p + "===undefined?" + defaults[i] + ":";
}
str += p + ";";
});
str += "return obj;";
str = "return new Function('" + props.join("','") + "','" + str + "')";
// Uses new Function to create the new function
return (new Function(str))(); // Is this dangerous???
}
})(); // wrapped in an anon function
Which lets me create objects without having to name all the properties and code in defaults.
Edit: Use of the above function.
var car = objectPump("colour,year,type", // objects property names
"white,2015,All Wheel Drive"); // object defaults
// or as arrays
var car = objectPump(["colour","year","type"], // objects property names
["white",2015,"All Wheel Drive"]); // object defaults
var cars = [
car("red",2011), // missing property defaults to All Wheel Drive
car("blue",2015,"bike"),
];
var aCar = car("blue",2015,"bike");
// same as
var aCar = {
colour:"blue",
year:2015,
type:"bike"
}; // but saves me having to type out the property names for each new object
To me it looks very similar to using eval and a place a third party harker could get some malicious code in. So far it has been very handy and I am tempted to use new Function for other tasks.
Should I use new Function() to generate code or is it considered bad and/or dangerous for public code.
var car = objectPump("colour,script", // objects property names
"white,\" + alert(\"test\") + \""); // object defaults
console.log(new car('blue, but the nice one')); // throws alert
Do you mean like this dangerous?
To be honest, I don't really like objectPump function. There are other viable options you have:
Use TypeScript and its default values for functions (http://www.typescriptlang.org/Handbook#functions-optional-and-default-parameters)
Use typeof for defining default values even though it's more typing:
function foo(a, b)
{
a = typeof a !== 'undefined' ? a : 42;
b = typeof b !== 'undefined' ? b : 'default_b';
...
}
(https://stackoverflow.com/a/894877/99256)
EDIT: The function objectPump does not give your attacker any advantage. 1) If your attacker can modify your JS file, then she will use eval straight away and she does not need any objectPump. 2) If you sanitize all input from your users, there is no problem here.
My primary concern here is that you will eventually shoot yourself in the foot, rather than an attacker will.

How can I remove certain elements of a query string?

I'm working on a script, where you pass it a url like /search?filter1=question-1&filter2=question2, and when either question-1 or question-2 is changed, it will take the url, and replace the question-x with the question value.
One thing I want to build in, is if the value is empty, I want it to remove the query string part. So for example, if question-1 has a value of something, but 2 doesn't have a value yet, the url will be /search?filter1=something.
What I thought would work would be something like this
$url.match('/([^?&]*)' + name.toSearch + '/g') // toSearch is a value like question-1
But that returns null. Can anybody help me figure out what I need to change to get the output I'm after?
Given the url /search?filter=question-1, I need to see if the element with the name question[1] has a value, if it does, replace question-1 with the value, and if it doesn't have one, remove the total filter=question-1 string.
Knowing your requirements better from the comments, I completely rewrote my answer using parts of my original answer and some of your code:
// Given url
var url = '/search?filter1=question-1&filter2=question-2';
// Specify the filters so no extra query string noise enters the final url
var filters = ["filter1", "filter2", "filter3"];
// Split the query string parts
var urlParts = url.split(/([&?])/);
var reassembled = [];
// Break the url parts into key:value pairs
var qs = (function(a) {
if (a === "") return {};
var b = {};
for (var i = 0; i < a.length; ++i)
{
var p=a[i].split('=', 2);
if (p.length == 1)
b[p[0]] = "";
else
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
}
return b;
})(urlParts);
// This include a param:value in the reassembled array
function includeQSParam(param, value) {
if(qs[param]) {
reassembled.push(param + "=" + value);
}
}
// Run through the filters
for(var ind in filters) {
var filter = filters[ind];
// Check that the filter exists and the supplied value is of type question-
if(qs[filter] && qs[filter].indexOf("question-") >= 0) {
// Turns question-number into question[number] so it's a valid selector.
var inputSelector = "question["+(qs[filter]).replace(/\D/g, "")+"]";
// Get the input, and the value (author-supplied code)
var $input = $('[name="' + inputSelector + '"]');
// TODO: confirm this is how you get the value
var value = $input.closest('.question').val();
if($input.length > 0 && (value !== '' && value !== undefined)) {
// Replace the parameter's original value with the question value
includeQSParam(filter, value);
} else {
// Nothing to do. This filter will be removed automatically
}
}
}
// Reassemble the URL
var fixedUrl = urlParts[0] + (reassembled.length > 0 ? "?"+reassembled.join("&") : "");
Again, this was reworked from my original answer so there will be some bloat, but I didn't want to abandon the question on you.
Whilst Drakes answer is a good one, it didn't quite fit into my needs. I've ended up with this, which works well so far, but I'm still testing it.
var $url = '/search?filter1=question-1&filter2=question-2';
// Break the url into parts.
var $split = $url.split(/([&?])/);
$.each($split, function(indexToRemove, part){
// If the part is a question.
if(typeof part == 'string' && part.indexOf('question-') > -1){
var $number = part.split('=');
// Turns question-number into question[number] so it's a valid selector.
$inputSelector = String($number[1]).replace('-', '[') + ']';
// Get the input, and the value.
var $input = $('[name="' + $inputSelector + '"]');
var $value = getValue($input.closest('.question'));
// If there is an element, and there is a value.
if($input.length > 0 && ($value != '' && $value != undefined)){
$split[indexToRemove] = part.replace($number[1], $value);
} else {
$split.splice(indexToRemove, 1);
}
}
});
$url = $split.join('');
// If for example question-1 has a value of 'Test', and question-2 has no value, $url will now be '/search?filter1=Test'.

jQuery .each: Do not add comma in last field

I want to get values of all fields in a variable separated by a comma. For example: 1,2,3
The following code will work fine, but it only adds the comma at end of the last value also. How can I remove that?
fields = $('.wrap').find('.text');
var data = '';
fields.each(function() {
var value = $(this).val();
if ( value != '' ) {
data += ' ' + value + ',';
}
});
alert(data);
JSFiddle:
http://jsfiddle.net/cn5Gt/
I always use arrays for these kind of things:
var fields = $('.wrap').find(".text[value!='']");
var data = [];
fields.each(function() {
data.push($(this).val());
});
alert(data.join(','));
You can push elements on array than just use join() method.
fields = $('.wrap').find('.text');
var data = [];
fields.each(function() {
var value = $(this).val();
if ( value != '' ) {
data.push(value);
}
});
alert(data.join());
Try the code below, using the i which is the loop index and test against the length of the jQuery object.
fields = $('.wrap').find('.text');
var length = fields.length;
var data = '';
fields.each(function(i) {
var value = $(this).val();
if ( value != '' ) {
if(i === length-1) { //The last one
data += ' ' + value;
} else {
data += ' ' + value + ',';
}
}
});
Updated fiddle
You could just remove the final character afterwards?
data = data.substr(0, data.length - 1);
http://jsfiddle.net/cn5Gt/3/
Simplest of all:just replace your last line with the below line
alert(data.slice(0,-1));//where data is string
http://jsfiddle.net/cn5Gt/7/
DOC MDN : slice
How about something like this:
var data = $('.wrap').find('.text')
.map(function(i,el){ return el.value || null; })
.get().join(", ");
Demo: http://jsfiddle.net/cn5Gt/11/
jQuery's .map() method will "Pass each element in the current matched set through a function, producing a new jQuery object containing the return values." You can still include your if ( value != '' ) { test in the callback function because if your function returns null or undefined then .map() will not use that particular value. (I've used || null above as a shortcut to an if/else structure.)
Call .get() (or .toArray()) on the result and you'll have an actual array, which means you can then use the Array .join() method to form a string with the values comma separated.
If you need to do other processing on each item besides just getting the value you could stick with the .each() loop and add the values to an array that you then join after the loop (like some of the other answers), or just use the string .slice() method to remove the trailing comma and space characters.
Try this:
Using the length function to determine the position of the each
var fields = $('.wrap').find('.text');
var len = fields.length;
var data = '';
fields.each(function(index, element) {
var value = $(this).val();
if ( value != '' ) {
data += ' ' + value;
if (index != len - 1) {
data += ',';
}
}
});
alert(data);

Check if JSON keys/nodes exist

I'm using the Google Map API to retrieve city + state/region information from a postal code lookup. The issue is that in some cases a postal code lookup won't retrieve a city name. An example is 92625 (U.S).
var g = new GClientGeocoder();
g.setBaseCountryCode('US');
g.getLocations('92625', function(response){
if (response) {
var place = response.Placemark[0];
var state = place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName;
var city = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName;
GLog.write("City = "+city+" : State/Region = "+state+" : Country = " + g.getBaseCountryCode());
}
});
In certain cases, as mentioned above, there won't be a city name in the result so there will be an undefined error for city, because the key Locality does not exist. This error prevents the rest of the script from running.
I was able to remedy it by...
if (place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality != null)
var city = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName;
else
var city = '';
...but this has me paranoid about a similar error for other keys. Eg: If AdministrativeArea is undefined the above IF statement would also cause an undefined error. So should I be checking to see if every Key/Node exists? Seems to be a messy approach because some of these keys are 5+ levels deep...is there an easier way to go about it, maybe some JQuery method I'm not familiar with?
Alternatively, you could make a function, that gives you defaults:
function valueOrDefault(val, def) {
if (def == undefined) def = "";
return val == undefined ? def : val;
}
And then use it like this:
var place = response.Placemark[0];
var state = valueOrDefault(place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName);
var city = valueOrDefault(place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName);
Personally, I think it's a little nicer to write, than p00ya's proposal, although it's a little hacky fiddling around in undefined objects ... one could maybe change it to this:
function drill(p, a) {
a = a.split(".");//add this
for (i in a) {
var key = a[i];
if (p[key] == null)
return '';
p = p[key];
}
return p;
}
var obj = {foo:{bar:{baz:"quux"}}};
var s = drill(obj, "foo.bar.baz"));//and you can use a simple property chain
You could use a function that "drills" down through all those nesting levels, defaulting to the empty string if it can't get that far.
function drill(p, a) {
for (i in a) {
var key = a[i];
if (p[key] == null)
return '';
p = p[key];
}
return p;
}
var obj = {foo:{bar:{baz:"quux"}}};
var s = drill(obj, ["foo", "bar", "baz"]));
I like back2dos' approach but I think it could be improved so as not to fail with ReferenceErrors:
function jPath(obj, a) {
a = a.split(".");
var p = obj||{};
for (var i in a) {
if (p === null || typeof p[a[i]] === 'undefined') return null;
p = p[a[i]];
}
return p;
}
// Tests
var a = {b:{c:'Hello World!'}, c:null};
console.log(jPath(a, 'b.c')); // Hello World
console.log(jPath(a, 'b.d')); // null
console.log(jPath(a, 'e.f.g')); // null
console.log(jPath(a, 'c')); // null
var z;
console.log(jPath(z, 'c')); // null
This kind of function is great for validating deep JSON return structures from AJAX services such as freebase or YQL.
You are looking at only the first result the geocoder gives you:
var place = response.Placemark[0];
getLocations() returns a list of several results. If the first one doesn't have it, one of the next few results almost certainly will.

Categories