Turn Coffeescript loop using range into ES6 - javascript

Disclaimer: I don't know Coffeescript and, although I appreciate it has contributed towards the ES6 spec, I can't wait to see the back it.
This Coffeescript loop (wrote by someone else)
if #props.total>1
for page in [1..#props.total]
active = (page is +#props.current)
is, according to js2coffee, equivalent to this JS
var active, i, page, ref;
if (this.props.total > 1) {
for (page = i = 1, ref = this.props.total; 1 <= ref ? i <= ref : i >= ref; page = 1 <= ref ? ++i : --i) {
active = page === +this.props.current;
}
}
Now I would like to use a for..of loop to shorten that JS, but I can't figure out how.
I've tried to implement this idea(the generator function bit at the bottom), but I can't get it right.
My question is: Is there a way of making ranges in ES6?

The generator solution you are looking for would be
function* range(i, end=Infinity) {
while (i <= end) {
yield i++;
}
}
// if (this.props.total > 1) - implicitly done by `range`
for (let page of range(1, this.props.total) {
active = page === +this.props.current;
}

For generating any range of sequential integers of length k starting at n in JavaScript the following should work:
Array.apply(null, Array(k)).map((x, i) => i + n);
While not quite the same as the coffeescript range functionality, its probably close enough for most uses. Also despite being significantly more verbose has one decided advantage: you don't have to remember which of .. and ... is exclusive and which is inclusive.

Related

How does the JS engine deal with explicitly typed computations?

I've been interested in meta-programming of late. We can take a generic computation, in this case the mean, and create an efficient, spelled out function on the fly for it. Here I'm creating, in mean2, a function that will compute the mean explicitly (without loops). Generally, these explicit functions run more quickly. But I'm experiencing some interesting behavior. In my timings, for an array of size 50 ran in 4e7 loops, the explicit function wins handily as anticipated:
symbolic: 2479.273ms | literal: 60.572ms
But nudge the initial array size from 50 to say 55, the performance dives precipitously.
symbolic: 2445.357ms | literal: 3221.829ms
What could be the cause of this?
const A = new Float64Array(50).map(Math.random)
const mean1 = function (A) {
let sum = 0
for (let i = 0; i < A.length; i++)
sum += A[i]
return sum / A.length
}
const mean2 = (function (A) {
return new Function('A', `
return (${new Array(A.length).fill(null).map(function (_, i) {
return `A[${i}]`
}).join('+')}) / ${A.length}
`)
})(A)
console.time('symbolic')
for (let i = 0; i < 4e7; i++)
mean1(A)
console.timeEnd('symbolic')
console.time('literal')
for (let i = 0; i < 4e7; i++)
mean2(A)
console.timeEnd('literal')
The result is being cached in the 2nd example, it has little to do with the "meta-programming" method, (or more specifically the lack of a loop).
This is not so easy to directly prove with JavaScript; in a compiled language you would usually just look at the assembly and see that the compiler has tried to be clever in some way and optimise out your loop inside the timer. JS runtimes are obviously more opaque and complex, the output is not so easily accessible. You can instead test for it by proxy, by observing the behaviour of different but equivalent forms to see what triggers the up-front optimisations.
The following slight modifications of your "mean2" function escape caching optimisations in nodejs (v8) at time of testing:
Assign to a scoped variable before return:
const mean2 = (function (A) {
return new Function('A', `
let sum = (${new Array(A.length).fill(null).map(function (_, i) {
return `A[${i}]`
}).join('+')}) / ${A.length};
return sum;
`)
})(A)
Use A.length reference instead of literal:
const mean2 = (function (A) {
return new Function('A', `
return (${new Array(A.length).fill(null).map(function (_, i) {
return `A[${i}]`
}).join('+')}) / A.length
`)
})(A)
You needn't worry about these subtitles though... instead change your testing methodology, ensure samples cannot be thrown away to produce the same result, otherwise you risk testing the compiler's ability to omit useless instructions instead of real performance.
In this case, re-initialise the array with random values before each sample, and to be absolutely sure - make some persistent variable dependent on the result of each sample. Also do not include these extra steps in the timer otherwise it will bias your sample time.

combination sum coding challenge

I am trying to solve a coding challenge "Combination Sum" (https://leetcode.com/problems/combination-sum/discuss/127208/Simple-Javascript-Solution) and found a solution but I can not understand why currArr.pop() inside if (target >= candidates[i]) is required. I tried to understand by removing it and replacing it but still can not figure it out. Could someone explain why this is necessary to me? Thanks!
var combinationSum = function(candidates, target) {
candidates.sort((a,b) => a - b);
const results = [];
const innerFunction = (currArr, target, index) => {
if (target === 0) {
results.push(currArr.slice());
return;
}
for (let i = index; i < candidates.length; i++) {
if (target >= candidates[i]) {
const newTarget = target - candidates[i];
currArr.push(candidates[i]);
innerFunction(currArr, newTarget, i);
currArr.pop();
} else {
return;
}
}
}
innerFunction([], target, 0);
return results;
};
console.log(combinationSum([2,3,6,7], 7));
This technique is called backtracking.
Backtracking is a technique used to build up to a solution to a problem incrementally. These "partial solutions" can be phrased in terms of a sequence of decisions. Once it is determined that a "partial solution" is not viable (i.e. no subsequent decision can convert it into a solution) then the backtracking algorithm retraces its step to the last viable "partial solution" and tries again.
the key point here is that after currArr.push(candidates[i]) flow is recurcion call innerFunction(currArr, newTarget, i); so currArr is constantly growing, at some point if (target === 0) desired result has found and you do deep copy of your currArr with currArr.slice() and now with return, code goes 1 step back to currArr.pop() since this come after innerFunction recursion call.
I hope this could help you -)

In Javascript, how do I obtain a reference to an element in an array?

I have a piece of code like
if (A[i][j] === 1)
A[i][j] = 0;
else
A[i][j] = 1;
and I'm wondering how I can optimize it like
var elem = A[i][j]; // obviously A[i][j] is an R-value ... I want an L-value
if (elem === 1)
elem = 0;
else
elem = 1;
In addition, I'm wondering if there's a single operation that can perform the equivalent of toggling between 0 and 1, the equivalent of
if (elem === 1)
elem = 0;
else
elem = 1;
Is there some fancy bitwise operation that can do it?
The comment by #Andreas is a good optimization. Turned into an answer for posterity:
A[i][j] ^= 1;
It only retrieves the value once and applies the result. You still can't get a memory address (JS is reference only), but it limits the number of times you have to access it.
The Bitwise XOR has the nice effect of turning 0 -> 1 and 1 -> 0 when applied with a 1 mask. By doing all the work in one op, you can get the fewest accesses possible.
Edit: As per the discussion, this solution only works when the array is filled with 0 / 1 values.

alternatives for excessive for() looping in javascript

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/

javascript function needs doubleclick to work

I have these two javascript functions that for some reason I have to click the button twice for them to work. any ideas?
var shirts = new Array("shirtpink.png","shirtred.png", "shirtblue.png", "shirtyellow.png","shirt.png");
var index = 0;
function changecolor(){
if (index > 4){
index = 0;
}
var shirtpick = document.getElementById("shirt");
shirtpick.setAttribute('src',shirts[index]);
index++;
}
other function:
function changecolorback(){
index--;
i++;
if (index < 0){
index = 4;
}
var shirtback = document.getElementById("shirt");
shirtback.setAttribute('src',shirts[index]);
}
var shirts = ["shirtpink.png","shirtred.png", "shirtblue.png", "shirtyellow.png","shirt.png"],
index = 0;
function changecolor(amount){
index = (index + amount) % shirts.length;
document.getElementById("shirt").setAttribute('src',shirts[index]);
}
The reason was your increments:
You were incrementing after your code executed (in one function, but not in the other, so your changecolorback() should have behaved ok).
You also had i++ which looked redundant, and some variables that were only used once.
I shortened your code (drastically) so you get to these comments without too much scrolling.
Instead of calling changecolorback(), you can now just do changecolor(-1), and the forward method is changecolor(1).
Another advantage is that this will allow you to jump by more than one, or a random amount, which might (or might not) be useful.
EDIT:
JS implements modulus like Java, so negatives still hold their sign. I.e. (-1)%5 === -1
You can overcome this pretty easily by changing (more elegant although not quite equivalent):
index = Math.abs(index + amount) % shirts.length;
or
index = ((index + amount) % shirts.length + shirts.length ) % shirts.length;
Try incrementing/decrementing index at the beginning of both functions. Also, you have 5 elements, not 4 in your array. You should probably use shirts.length to avoid this.

Categories