Javascript Eval to work for both function and plain string - javascript

I want to display a list of items, now sometimes these items' title will just be a plain string, and sometimes it might be a value returned by a function.
How can I make both events work using eval() ?
Here is an example code:
var a1 = "formatDate('" + startTime + "') + ' - ' + formatDate('" + endTime + "')"
var a2 = "#america"
var result1 = eval(a1) // works well!
var result2 = eval(a2) // doesn't work, need to use eval('a2') but then first one doesn't work
Only thing I can think of is when creating the string for example "#america" have it saved like "'#america'" instead, but I would rather avoid it
[edit]
Eventually I will have something like this:
arr.push("formatDate('" + startTime + "') + ' - ' + formatDate('" + endTime + "')");
arr.push("#america");
for(var i = 0; i < arr.length; i++) {
var ev = eval(arr[i]);
console.log(ev);
}

What I would suggest is wrapping the eval in a try catch block, if the eval succeeds then return the value otherwise return the value originally passed to function. There are many cases where eval can fail as it is simply trying to parse the string as valid JavaScript so any invalid JS not just a simple string can cause it to fail so its better to be safe and catch any error that comes out of it.
var evaluate = function(value) {
try {
return eval(value);
}
catch(err)
{
return value;
}
}

var ev = eval(a2) would be equivalent to var ev = eval('#america') which doesn't make any real sense.
When you say eval('a2') works, I assume that ev = '#america' is the desired outcome. The 'a2' expression is evaluated as simply accessing the value of the variable of that name.
You're basically just having a series of strings that may be valid javascript code, or may not, and there's no way to tell which is which. In that case, the best you can do is something like
try {
ev = eval(arr[i]);
} catch(ex) {
ev = arr[i];
}
... which obviously looks terrible. Can you control the content of the entries in arr?
arr.push(function() {
return formatDate(startTime) - formatDate(endTime);
});
arr.push("#america");
In that case, you could check for the type of each entry, and act on it accordingly:
for(var i = 0; i < arr.length; i++) {
var ev = typeof arr[i] == 'function' ? arr[i]() : arr[i];
console.log(ev);
}

this is that i should do:
var a1 = function(){
return formatDate(startTime) + formatDate(endTime)
}
var a2 = "#america"
var result1 = a1();
var result2 = a2;
Yo can check with typeof(a1) if the var is a function or an object or anyting else.
if(typeof(a1)=='function'){
result = a1();
}else{
result=a1;
}

Related

When parsing XML with recursive function, how do I return a string or array from that function?

I've got a working recursive function which goes through an XML doc looking for a matching node name, and then logging matching values...I'm trying to modify it to return a string or an array, and can't figure it out.
This is in Google Apps script. I've tried passing in a blank string into the function, and then returning it at the end, but it doesn't work. Here is the working Logger function:
function logChildren(elements, dataRequired){
for (var i = 0; i < elements.length; i++) {
if (elements[i].getName() == dataRequired){
Logger.log(elements[i].getText());
}
if(elements[i].getContentSize() > 1){
var children = elements[i].getChildren();
logChildren(children, dataRequired);
}
}
};
I tried passing in an empty string, and then returning it like this but it doesn't work:
function logChildren(elements, dataRequired, str){
for (var i = 0; i < elements.length; i++) {
if (elements[i].getName() == dataRequired){
str = str + ", " + elements[i].getText();
}
if(elements[i].getContentSize() > 1){
var children = elements[i].getChildren();
logChildren(children, dataRequired, str);
}
}
return str
};
How do I get a string or array OUT of this function, rather than just console logging it?
Instead of returning str try without it, because str will have all the values. If you return str it might collapse the current iteration. Please let us know whether this worked
Providing your elements is already parsed and valid, this should work.
function logChildren(elements, dataRequired){
values = [];
req = elements.getElementsByTagName(dataRequired);
for (var i = 0; i < req.length; i++) {
values.push(req[i].childNodes[0].nodeValue);
}
return values
};
elements = "<house>" +
"<name>hello</name>" +
"<town>world</town>" +
"<name>cat</name>" +
"<folder>" +
"<name>kitty</name>" +
"</folder>" +
"</house>";
p = new DOMParser();
elements = p.parseFromString(elements, "text/xml");
newValues = logChildren(elements, "name")
console.log(newValues);
I've included my own little xml just to test, and it returns an array.
As you can see, getElementsByTagName even returns values in sub folders.
You should use a global variable or another function, so that the output variable str is outside the scope of the recursed function.
var str = "";//holds all data of recursion
function logChildren(elements, dataRequired){
..
str += ", " + elements[i].getText();
..
}

How to remove function name from Javascript object

I have a Leaflet map and I want to edit a polygon. I successfully do this, but when I finish the editing, the coordinates are saved like:
,,LatLng(44.94633, 26.00773),LatLng(44.93588, 25.94318),LatLng(44.94245, 25.90645),LatLng(44.91814, 25.87074),LatLng(44.91328, 25.9346),LatLng(44.90015, 25.97031),LatLng(44.90112, 26.11519)"
I only want to have the coordinates without function name. How can I do this? Thanks!
map.on("dragend", function(e){
poligon = polygon.getLatLngs();
poligon1 = poligon.toString();
$('#geo').val(poligon1);
console.log(poligon1);
});
Dont use toString() u will get an array of objects
var arr=[];
console.log(polygon.getLatLngs());
for(var i=0;i<arr.length;i++){
arr=polygon.getLatLngs();
console.log(arr[i].lat);
console.log(arr[i].lng);
console.log("("+arr[i].lat +","+arr[i].lng+")");
}
Resolved it by writing one line:
poligon = polygon.getLatLngs();
//this is what I added
poligon2=poligon.join(',').match(/([\d\.]+)/g).join(',')
You can override toString method of LatLng prototype at your project init
L.LatLng.prototype.toString = function() {
return '(' + this.lat + ',' + this.lng + ')';
}
Then you'll see output like this cause Array.toString() recursively call toString() on every element in collection.
(44.94633, 26.00773),(44.94633, 26.00773)
I'll just add an answer.
This should work in general: give it a string, it will try to find all numbers, and return them in an array.
<script>
var mystring = "LatLng(44.94633, 26.00773),LatLng(44.93588, 25.94318),LatLng(44.94245, 25.90645),LatLng(44.91814, 25.87074),LatLng(44.91328, 25.9346),LatLng(44.90015, 25.97031),LatLng(44.90112, 26.11519)";
function isNumeric(input) {
return (input - 0) == input && input.length > 0;
}
// reads a string, finds numbers (float), returns the numbers in an array
function numbersInString(string) {
var s = 0, temp=0, result = [];
for(var i=0; i<string.length; i++) {
s = string.substr(i,1); // search 1 character, see if it's a number (digit)
if(isNumeric(s)) {
// parseFloat wil read as many characters as it can, and drop the rest
temp = parseFloat(string.substr(i));
// okay, now skip the length of the float
i = i + temp.toString().length ;
result.push(temp);
}
}
return result;
}
window.onload = function() {
var numbers = numbersInString(mystring);
document.getElementById('log').innerHTML += numbers.join(',');
}
</script>
<div id="log"></div>

Implementing wildcard at end of a string

I looked through a number of posts (and other websites) and I seem to have a hit a roadblock. I have the following array:
var data_dictionary = ["youtube.com", "facebook.com", "youtube.com/feed/subscriptions", "twitter.com"]
I'm trying to return data for everything that has youtube.com*. Below is the relevant snippet of my function:
var result = []
for (var i=0; i<data_dictionary.length; i++) {
if (data_dictionary[i].page == /^youtube.com/) {
result.push (data_dictionary[i].page,data_dictionary[i].share)
}
}
break;
}
return result
The problematic area is in the if clause (/^youtube.com/). How can I receive the following return:
["youtube.com" , "youtube.com/feed/subscriptions"]
You can use Array.prototype.filter() method to filter array and RegExp.prototype.test() to check for match.
var data_dictionary = ["youtube.com", "facebook.com", "youtube.com/feed/subscriptions", "twitter.com"];
function check(data_dictionary) {
return data_dictionary.filter(function(v) {
return /^youtube\.com/.test(v);
// using indexOf
// v.indexOf('youtube.com') == 0;
});
}
console.log(check(data_dictionary));
FYI: Your if condition will be only true if the string is '/^youtube.com/'. ie, ('/^youtube.com/' == /^youtube.com/) === true. Your code will work if you changed the if condition to /^youtube.com/.test(data_dictionary[i]). Also in the provided data page and share properties are undefined only plain strings are the element.
Using the same approach that you had before. However using ".filter" won't be a bad idea, but I will suggest you compare their benchmark
var data_dictionary = ["youtube.com", "facebook.com", "youtube.com/feed/subscriptions", "twitter.com"];
var pattern = /^youtube.com/;
var result = [];
var i = 0;
function loop (args) {
for (i; i < args.length; i++) {
if (pattern.test(args[i])) {
result.push(args[i]);
}
}
return result;
}
console.log(loop(data_dictionary)) // ["youtube.com" , "youtube.com/feed/subscriptions"]
Comparing the speed below I would suggest you use the approach above
No need for regex here you can do like this;
var data_dictionary = ["youtube.com", "facebook.com", "youtube.com/feed/subscriptions", "twitter.com"],
filtered = data_dictionary.filter(e => !!~e.indexOf("youtube.com") && e);
document.write("<pre>" + JSON.stringify(filtered) + "</pre>");
Or if you want a faster solution still with Array methods then
var data_dictionary = ["youtube.com", "facebook.com", "youtube.com/feed/subscriptions", "twitter.com"],
filtered = data_dictionary.reduce((p,c) => !!~c.indexOf("youtube.com") ? p.concat(c):p,[]);
document.write("<pre>" + JSON.stringify(filtered) + "</pre>");

Assign an array value to another array called dynamically

I've got an array like this
pages['name'] = "Home";
pages['childs'][0]['name'] = "Sub page 1";
pages['childs'][1]['name'] = "Sub page 2";
pages['childs'][2]['name'] = "Sub page 3";
pages['childs'][2]['childs'][0]['name'] = "Sub sub page 1";
My problem is that I need to change portions of the array for example.
pages['childs'][0] = otherarray;
// or
pages['childs'][2]['childs'][0] = otherarray;
Obviously if otherarray was a string I can easily do something like
eval('pages' + where + ' = "' + stringvalue + '"');
But I've an array as value so I can't do
eval('pages' + where + ' = "' + otherarray + '"');
because the code executed will be
pages['childs'][0] = [object object];
What's the solution? Thanks
Rather than screw around with eval and stringifying things, you should just build an accessor. Many would agree that using eval like this is just bad practice in every way. I don't know if combining it with stringify makes it worse, but it certainly feels dirty.
Here's a basic, fairly stupid accessor, but it should give you the idea.
// Arguments: array to modify; new value; series of nested array keys.
function modifyArray(base, value){
var refObj = base;
for (var ii=2, max=arguments.length; ii < max; ii++){
if (!refObj) {
return false; // we supplied an invalid key.
}
if (ii == max-1){
refObj[arguments[ii]] = value;
return true;
}
refObj = refObj[arguments[ii]];
}
return false; // probably forgot to include keys.
}
modifyArray(pages, otherarray, 'childs', 2, 'childs', 0);
http://jsfiddle.net/2ts78brg/
For me this solution works
eval("pages" + where + " = JSON.parse('" + JSON.stringify(otherarray) + "')");
It sounds more like a workaround then a solution but it works and for me it's enough.

Send #Model variable as parameter to a JavaScript function

start reading at 'UPDATE'
I have a JavaScript function :
<script type="text/javascript">
function getAmount(AdminFee, TimeAndMatRate, TimeSpent, MinimumCharge) {
var amount = math.Round(AdminFee + TimeAndMatRate * TimeSpent);
if (amount < MinimumCharge) {
amount = math.Round(MinimumCharge);
}
document.getElementById('amount').innerHTML = amount;
}
</script>
And I have the following field that has to be changed when I change another value:
<span id="amount"></span>
When I change a value in this textbox the above field should be updated:
#Html.TextBoxFor(model => model.TimeAndMaterialsRate, new { onkeyup = "getAmount()" })
This code works fine as long as I do not try to send any parameters in getAmount(), but I can't seem to figure out how to get it to work, could anyone please help me?
This is one of the things that I tried
new { onkeyup = "getAmount("
+ #Model.AdminFee + ","
+ #Model.TimeAndMaterialsRate + ","
+ #Model.TotalTimeSpent.Hours + ","
+ #Model.MinimumCharge + ")"
}
I also tried to above without '#'s
UPDATE:
The above code works now although it seems the function doesn't work properly.
<script type="text/javascript">
function getAmount(AdminFee, TimeAndMatRate, TimeSpent, MinimumCharge) {
var amount = math.Round(AdminFee + TimeAndMatRate * TimeSpent);
if (amount < MinimumCharge) {
amount = math.Round(MinimumCharge);
}
document.getElementById('amount').innerHTML = amount;
}
</script>
The values passed into the function are id, id, double, id. Any clue on why this doesn't work?
I think your problem is a slight syntax error with the end of your string. The last plus sign is in the wrong place. It should be like this:
new { onkeyup = "getAmount("+#Model.AdminFee+","+#Model.TimeAndMaterialsRate+","+#Model.TotalTimeSpent+","+#Model.MinimumCharge+")" })
I'd say to use eval, but it's preety bad practice because it can be really injected & stuff.
Look at this article http://www.sitepoint.com/call-javascript-function-string-without-using-eval/
What you need is :
// function name and parameters to pass
var fnstring = "runMe";
var fnparams = [1, 2, 3];
// find object
var fn = window[fnstring];
// is object a function?
if (typeof fn === "function") fn.apply(null, fnparams);
Your params will look like :
var fnparams = {
minimumCharge : '#Model.MinimumCharge',
... // and so on
};
And you will get them in this way :
parseInt(fnparams.minimumCharge);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt

Categories

Resources