I am trying to recursively print out the contents of jQuery. I'm planning on using this to analyze an existing instance of jQuery (loaded in a page) against a small database of known "jQuery signatures", to determine what changes have been made (i.e. what plugins have been loaded, functions modified, etc.).
To do this, I have this small function:
function recurse(obj, iter){
var padding = (new Array(iter + 1)).join(" ") + ">";
for (var i in obj){
document.writeln(padding + i + "<br/>");
if (iter < 5)
recurse(obj[i], iter + 1);
}
}
When I execute this:
recurse(jQuery, 1);
I get something like this:
>prototype
>init
>prototype
>init
>prototype
>selector
>jquery
>0
.... On and on and on .....
My problem is, at the very beginning, you can see that prototype and then init repeat over and over again. The only reason it stopped at 5 deep is because of the if (iter < 5) check. If the limit wasn't there, it would have recurred [sic?] forever. The iteration limit helps, but what if there is a critical function 6 deep? Essentially, I have no idea what I should make this iteration limit, or if there should be one at all.
Instead, I'm thinking there must be some kind of algorithm that can prevent never-ending recursion. Does such an algorithm exist? Or should I change how I go about traversing jQuery? Thanks,
You could keep track of what values you've already seen, and just bail out when you see one again.
function recurse(obj) {
var marker = '__' + new Date().getTime() + '__';
function r(obj, iter) {
if (marker in obj) return;
var padding = (new Array(iter + 1)).join(" ") + ">";
obj[marker] = true;
for (var i in obj) {
if (!obj.hasOwnProperty(i) || i === marker) continue;
document.writeln(padding + i + "<br/>");
recurse(obj[i], iter + 1);
}
}
r(obj, 0);
}
Now this of course has the disadvantage of leaving your traversed object graph littered with extra properties, but for some applications that wouldn't be a problem. Also using the clock to make a "unique" marker is really lame; you might want to just use a counter, or maybe just a fixed nonsense string.
edit — Also, another issue (present in the original code too) is that this really should be checking to see if the "obj" values are really objects. If they're scalars, then there's no point doing anything, really. You'd just need to do a "typeof" check right after checking for the marker, and if you see null, a number, a string, or a boolean, just return.
Your recursion is missing a base case. See the definition of Recursion. You introduced an arbitrary base case of (depth < 5). Maybe instead use the length of the array, or as Pointy pointed out, the hasOwnProperty check to skip the recursive call.
Unless i am missing something, all you need to do is to skip strings (if you use a modern browser that allows indexing of strings, otherwise it doesn't matter) and the init function which is the jQuery self reference that gives you the infinite recursion
function recurse(obj, iter){
var padding = (new Array(iter + 1)).join(" ") + ">";
for (var i in obj){
document.writeln(padding + i + "<br/>");
if (i != 'init' && typeof obj[i] != 'string')
recurse(obj[i], iter + 1);
}
}
As several answers said: You algorithm must contain a proper break case to prevent infinite recursion.
But what happen if you cannot control the input of your algorithm?
For that case, or just for development purposes, you could use this:
function acmeRecursion(){
var count = localStorage.getItem("count");
if(count>50){
throw new Error("recursion limit exceeded");
}
count++;
//put your logic here
}
Building off of Pointy's answer (ideally this would be a comment, but alas, code doesn't work great in those), a better solution might be to just pass along an object to the the recurse function that will keep track of objects you've already seen. Something like this:
var recurse = function(obj)
{
var seen = {};
var inner = function(obj, padding)
{
for (var i in obj)
{
if (!(obj[i] in seen))
{
document.writeln(padding + i + '<br />');
seen[obj[i]] = true;
inner(obj[i], padding + ' ');
}
}
};
return inner(obj, '');
};
Which uses a closure rather than an argument to pass along the seen object, for the sake of simplicity, but the basic concept is the same.
This approach has the advantage of not adding extra attributes to the objects you're traversing.
Edit: I meant to explain this, but forgot. I'm not using hasOwnProperty here because in the case of printing an object graph, you probably do want to see inherited attributes.
Related
I have a big question.
I have many Strings in my Programm and want to check these Strings on there values.
I wrote a Loop for it, but insted of the Definition of an String he is creating a new value. It's basicly really difficult to discribe, also because i am basicly German.
But i can give you my current code, so maybee you will see what I mean:
{
var Loch1G = $('#m1-Rundenanalyse-Datum').val(); //In the strings just the number is changing
var Loch2G = $('#m1-Rundenanalyse-Turnier').val();
x=1
while (x <= 2) {
if ("Loch" + x + "G" == ""){ //Next String is genrated (x=x+1)
alert("Eingabe war leer");
}
x=x+1
}
}
How can I solve this?
I'd suggest using an array to store the values you want to check:
var lochs = [];
lochs.push($('#m1-Rundenanalyse-Datum').val());
lochs.push($('#m1-Rundenanalyse-Turnier').val());
for (var i = 0, len = lochs.length; i < len; i++){
if (lochs[i] == ''){
alert("Eingabe war leer");
}
}
JS Fiddle demos: passes (no alert), fails (alert)
This suggestion is based on my presumption that you're trying to create the names of the vars you want to check, which won't work, whereas this approach lets you store all values (however many) in the same array and then iterate over that array to find any values that are equal to an empty string.
If you really want to stick with your current approach, you could do the following:
{
window.Loch1G = $('#m1-Rundenanalyse-Datum').val(); //In the strings just the number is changing
window.Loch2G = $('#m1-Rundenanalyse-Turnier').val();
var x=1;
while (x <= 2) {
if (window["Loch" + x + "G"] == ""){ //Next String is genrated (x=x+1)
alert("Eingabe war leer");
}
x=x+1;
}
}
But I can't think why you'd want to; plus the use of global variables is poor practice as it explicitly makes those variables available to every closure within the document, which allows them to be easily, and accidentally, overwritten.
In a reasonably up-to-date browser, that implements Array.prototype.every, you could dispense with the explicit iteration:
var lochs = [];
lochs.push($('#m1-Rundenanalyse-Datum').val());
lochs.push($('#m1-Rundenanalyse-Turnier').val());
if (!lochs.every(function(a){ return a !== ''; })) {
alert("Eingabe war leer");
}
JS Fiddle demos: passes (no alert), fails (alerts).
I have this snippet of code which I simplified for the sake of this question:
//var a is generated once at runtime
//from an array of strings
//and an array of functions
var a = [
"Start ",
function(){return "middle ";}, //returns dynamic data
"end"
],
c = "";
//this for-loop represents a call
for(var i = 0; i < a.length; i++){
var d = typeof a[i] === 'function' ? a[i]() : a[i];
c = c.concat(d);
}
console.log(c);
Now first question: Is my call (the for loop) less or more optimal than say...
var call = a[0]+a[1]()+a[2];
...where call was created at runtime somehow?
Conditional question: If the latter call is the more optimal approach then how would I go about generating this optimal call variable/object/function?
Final conditional question: If you can't figure out what I'm asking in my last question, ignore it. Instead, please tell me if my code is able to be further optimized and how?!
Edit: I went ahead and benchmarked my code by running 24M calls and the call variable is about 10% faster from my estimations.
Though correct, and fine in terms of performance, this code doesn't feel right to me. What I'd do would be:
var a = [
"Start ",
function(){return "middle ";}, //returns dynamic data
"end"
];
var c = a.map(function(item) {
return typeof item === 'function' ? item() : item;
}).join('');
console.log(c);
This seems better to me than appending to your string in each for iteration.
The main answer here is: The for loop isn't going to introduce any kind of performance penalty you'll ever notice.
But:
If a is generated at runtime, you must have something, somewhere generating it. If so, rather than an array, why not just generate a function directly?
var acall = function() {
return "Start, " + dynamic() + "end";
};
or
var acall = function() {
return "Start, " + (function() {
return "middle ";
})() + "end";
};
The crux, really, is how a is generated at runtime. You can usually generate functions at runtime without resorting to eval or its cousin new Function...
I have an applet that allows the user to define values for 10 variables, all of which could be arrays. I then want the program to iterate over all possible combinations and then save the calculations to a variable.
Previously I hard-coded it to initialize the result array, SIGMA, by looping over every variable, regardless of if it is a vector or a single value, IE:
SIGMA = new Array(A.length);
for (i1=0; i1<A.length; i1++) {
SIGMA[i1] = new Array(B.length);
for (i2=0; i2<B.length; i2++) {
SIGMA[i1][i2] = new Array(C.length);
for (i3=0; i3<C.length; i3++) {
...
}
}
}
This results in a 10-dimensional SIGMA array, which makes processing really slow if a few or more variables are arrays.
What I'd like to do is to have it initialize SIGMA only for those variables that are an array and not a singular value. Therefore, if all variables are a single number except for two, say X and Y, then I'd want to have:
SIGMA = new Array(X.length);
for (i1=0; i1<X.length; i1++) {
SIGMA[i1] = new Array(Y.length);
}
However, I'm not quite sure how the best way to do this would be, as the number of for loops would depend on the number of variables that are arrays. I'm thinking I either need to use a recursive function or somehow incorporate a while loop.
Anybody have any good ideas on how this can be done? Thanks!
I was able to solve this problem by making a recursive function that included the for loops:
sigma = new Array(eval(var_idx[0]).length);
sigma_forloop('sigma', 0)
function sigma_forloop(X, idx) {
for (var i=0; i<eval(var_idx[idx]).length; i++) {
eval(X + '[' + i + ']' + ' = new Array(eval(var_idx[idx+1]).length);')
if (idx+2 < var_idx.length) {
var Y = X + '[' + i + ']';
sigma_forloop(Y, idx+1);
}
}
}
The variable 'var_idx' in an array of strings containing the variables that have more than one value, therefore, it's those variables that I want to loop over.
I'm sure there's an easier way of doing it, but this works for now.
Situation
I'm currently writing a javascript widget that displays a random quote into a html element. the quotes are stored in a javascript array as well as how many times they've been displayed into the html element. A quote to be displayed cannot be the same quote as was previously displayed. Furthermore the chance for a quote to be selected is based on it's previous occurences in the html element. ( less occurrences should result in a higher chance compared to the other quotes to be selected for display.
Current solution
I've currently made it work ( with my severely lacking javascript knowledge ) by using a lot of looping through various arrays. while this currently works ( !! ) I find this solution rather expensive for what I want to achieve.
What I'm looking for
Alternative methods of removing an array element from an array, currently looping through the entire array to find the element I want removed and copy all other elements into a new array
Alternative method of calculating and selecting a element from an array based on it's occurence
Anything else you notice I should / could do different while still enforcing the stated business rules under Situation
The Code
var quoteElement = $("div#Quotes > q"),
quotes = [[" AAAAAAAAAAAA ", 1],
[" BBBBBBBBBBBB ", 1],
[" CCCCCCCCCCCC ", 1],
[" DDDDDDDDDDDD ", 1]],
fadeTimer = 600,
displayNewQuote = function () {
var currentQuote = quoteElement.text();
var eligibleQuotes = new Array();
var exclusionFound = false;
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i];
if (exclusionFound === false) {
if (currentQuote == iteratedQuote[0].toString())
exclusionFound = true;
else
eligibleQuotes.push(iteratedQuote);
} else
eligibleQuotes.push(iteratedQuote);
}
eligibleQuotes.sort( function (current, next) {
return current[1] - next[1];
} );
var calculatePoint = eligibleQuotes[0][1];
var occurenceRelation = new Array();
var relationSum = 0;
for (var i = 0; i < eligibleQuotes.length; i++) {
if (i == 0)
occurenceRelation[i] = 1 / ((calculatePoint / calculatePoint) + (calculatePoint / eligibleQuotes[i+1][1]));
else
occurenceRelation[i] = occurenceRelation[0] * (calculatePoint / eligibleQuotes[i][1]);
relationSum = relationSum + (occurenceRelation[i] * 100);
}
var generatedNumber = Math.floor(relationSum * Math.random());
var newQuote;
for (var i = 0; i < occurenceRelation.length; i++) {
if (occurenceRelation[i] <= generatedNumber) {
newQuote = eligibleQuotes[i][0].toString();
i = occurenceRelation.length;
}
}
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i][0].toString();
if (iteratedQuote == newQuote) {
quotes[i][1]++;
i = quotes.length;
}
}
quoteElement.stop(true, true)
.fadeOut(fadeTimer);
setTimeout( function () {
quoteElement.html(newQuote)
.fadeIn(fadeTimer);
}, fadeTimer);
}
if (quotes.length > 1)
setInterval(displayNewQuote, 10000);
Alternatives considered
Always chose the array element with the lowest occurence.
Decided against this as this would / could possibly reveal a too obvious pattern in the animation
combine several for loops to reduce the workload
Decided against this as this would make the code to esoteric, I'd probably wouldn't understand the code anymore next week
jsFiddle reference
http://jsfiddle.net/P5rk3/
Update
Rewrote my function with the techniques mentioned, while I fear that these techniques still loop through the entire array to find it's requirements, at least my code looks cleaner : )
References used after reading the answers here:
http://www.tutorialspoint.com/javascript/array_map.htm
http://www.tutorialspoint.com/javascript/array_filter.htm
http://api.jquery.com/jQuery.each/
I suggest array functions that are mostly supported (and easily added if not):
[].splice(index, howManyToDelete); // you can alternatively add extra parameters to slot into the place of deletion
[].indexOf(elementToSearchFor);
[].filter(function(){});
Other useful functions include forEach and map.
I agree that combining all the work into one giant loop is ugly (and not always possible), and you gain little by doing it, so readability is definitely the winner. Although you shouldn't need too many loops with these array functions.
The answer that you want:
Create an integer array that stores the number of uses of every quote. Also, a global variable Tot with the total number of quotes already used (i.e., the sum of that integer array). Find also Mean, as Tot / number of quotes.
Chose a random number between 0 and Tot - 1.
For each quote, add Mean * 2 - the number of uses(*1). When you get that that value has exceeded the random number generated, select that quote.
In case that quote is the one currently displayed, either select the next or the previous quote or just repeat the process.
The real answer:
Use a random quote, at the very maximum repeat if the quote is duplicated. The data usages are going to be lost when the user reloads/leaves the page. And, no matter how cleverly have you chosen them, most users do not care.
(*1) Check for limits, i.e. that the first or last quota will be eligible with this formula.
Alternative methods of removing an array element from an array
With ES5's Array.filter() method:
Array.prototype.without = function(v) {
return this.filter(function(x) {
return v !== x;
});
};
given an array a, a.without(v) will return a copy of a without the element v in it.
less occurrences should result in a higher chance compared to the other quotes to be selected for display
You shouldn't mess with chance - as my mathematician other-half says, "chance doesn't have a memory".
What you're suggesting is akin to the idea that numbers in the lottery that haven't come up yet must be "overdue" and therefore more likely to appear. It simply isn't true.
You can write functions that explicitly define what you're trying to do with the loop.
Your first loop is a filter.
Your second loop is a map + some side effect.
I don't know about the other loops, they're weird :P
A filter is something like:
function filter(array, condition) {
var i = 0, new_array = [];
for (; i < array.length; i += 1) {
if (condition(array[i], i)) {
new_array.push(array[i]);
}
}
return new_array;
}
var numbers = [1,2,3,4,5,6,7,8,9];
var even_numbers = filter(numbers, function (number, index) {
return number % 2 === 0;
});
alert(even_numbers); // [2,4,6,8]
You can't avoid the loop, but you can add more semantics to the code by making a function that explains what you're doing.
If, for some reason, you are not comfortable with splice or filter methods, there is a nice (outdated, but still working) method by John Resig: http://ejohn.org/blog/javascript-array-remove/
Is there an equivalent of IEnumerable.Any(Predicate<T>) in JavaScript or jQuery?
I am validating a list of items, and want to break early if error is detected. I could do it using $.each, but I need to use an external flag to see if the item was actually found:
var found = false;
$.each(array, function(i) {
if (notValid(array[i])) {
found = true;
}
return !found;
});
What would be a better way? I don't like using plain for with JavaScript arrays because it iterates over all of its members, not just values.
These days you could actually use Array.prototype.some (specced in ES5) to get the same effect:
array.some(function(item) {
return notValid(item);
});
You could use variant of jQuery is function which accepts a predicate:
$(array).is(function(index) {
return notValid(this);
});
Xion's answer is correct. To expand upon his answer:
jQuery's .is(function) has the same behavior as .NET's IEnumerable.Any(Predicate<T>).
From http://docs.jquery.com/is:
Checks the current selection against an expression and returns true, if at least one element of the selection fits the given expression.
You should use an ordinary for loop (not for ... in), which will only loop through array elements.
You might use array.filter (IE 9+ see link below for more detail)
[].filter(function(){ return true|false ;}).length > 0;
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
I would suggest that you try the JavaScript for in loop. However, be aware that the syntax is quite different than what you get with a .net IEnumerable. Here is a small illustrative code sample.
var names = ['Alice','Bob','Charlie','David'];
for (x in names)
{
var name = names[x];
alert('Hello, ' + name);
}
var cards = { HoleCard: 'Ace of Spades', VisibleCard='Five of Hearts' };
for (x in cards)
{
var position = x;
var card = card[x];
alert('I have a card: ' + position + ': ' + card);
}
I suggest you to use the $.grep() method. It's very close to IEnumerable.Any(Predicate<T>):
$.grep(array, function(n, i) {
return (n == 5);
});
Here a working sample to you: http://jsfiddle.net/ErickPetru/BYjcu/.
2021 Update
This answer was posted more than 10 years ago, so it's important to highlight that:
When it was published, it was a solution that made total sense, since there was nothing native to JavaScript to solve this problem with a single function call at that time;
The original question has the jQuery tag, so a jQuery-based answer is not only expected, it's a must. Down voting because of that doesn't makes sense at all.
JavaScript world evolved a lot since then, so if you aren't stuck with jQuery, please use a more updated solution! This one is here for historical purposes, and to be kept as reference for old needs that maybe someone still find useful when working with legacy code.
Necromancing.
If you cannot use array.some, you can create your own function in TypeScript:
interface selectorCallback_t<TSource>
{
(item: TSource): boolean;
}
function Any<TSource>(source: TSource[], predicate: selectorCallback_t<TSource> )
{
if (source == null)
throw new Error("ArgumentNullException: source");
if (predicate == null)
throw new Error("ArgumentNullException: predicate");
for (let i = 0; i < source.length; ++i)
{
if (predicate(source[i]))
return true;
}
return false;
} // End Function Any
Which transpiles down to
function Any(source, predicate)
{
if (source == null)
throw new Error("ArgumentNullException: source");
if (predicate == null)
throw new Error("ArgumentNullException: predicate");
for (var i = 0; i < source.length; ++i)
{
if (predicate(source[i]))
return true;
}
return false;
}
Usage:
var names = ['Alice','Bob','Charlie','David'];
Any(names, x => x === 'Alice');