Since I am still fairly new to js I thought it couldn't hurt to ask more experienced coders about ways to improve my coding habits and to learn efficient basics.
So im wondering if I could run, say 2 lines of code in a loop x amount of times and then x amount of times on the rest of the block.
So instead of this:
for (let i = 0; i <= 10; i++) {
this.shapes[i].x -= 1;
this.shapes[i].draw(this.ctx);
}
for (let i = 0; i <= 10; i++) {
this.shapes[i].x += 1;
this.shapes[i].draw(this.ctx);
}
Does something like this exist?
for (let i = 0; i <= 10; i++) {
//run this section i amount of times
this.shapes[i].x -= 1;
this.shapes[i].draw(this.ctx);
//then run this i amount of times
this.shapes[i].x += 1;
this.shapes[i].draw(this.ctx);
}
The only difference between the two loops' bodies seems to be one statement.
You can use some math to determine the index and some logical statements to determine if that value should be incremented or decremented, here is an example:
for (let i = 0; i <= 21; i++) {
const index = i % 11;
this.shapes[index].x += (i > 10) ? 1 : -1;
this.shapes[index].draw(this.ctx);
}
If it's exactly the same code you can refactor it like this:
for (let delta of [-1, +1]) {
for (let i = 0; i <= 10; i++) {
this.shapes[i].x += delta;
this.shapes[i].draw(this.ctx);
}
}
Another option is to use a function using delta as a parameter
changeShapeByDelta = (delta) => {
for (let i = 0; i <= 10; i++) {
this.shapes[i].x += delta;
this.shapes[i].draw(this.ctx);
}
}
changeShapeByDelta(-1);
changeShapeByDelta(+1);
Another option is to deep copy your initial shapes and restore it after the first draw.
You could ofc. define more variables then i:
for (let i = 0, j=0; i <= 10; i++, j+= 2) {
console.log(i, j);
}
Or use another var in the parent scope:
let j = 0;
for (let i = 0, j=0; i <= 10; i++, j+= 2) {
j += 2
console.log(i, j);
}
Or just use plain old if,else and break statements. Code does not have to look nice all the time.
You can loop 20 times instead of 10 times and then run first code if i<10 else run second. Below is example with simple logging.
for(let i = 0;i<22;i++){
if(i<11) console.log('first')
else console.log('second')
}
I have the following code which follows a pattern of loops , I have a feeling that code can be minified to a recursion like code or any less ugly looking code , but I am unable to figure it out.
I want to run six loops one inside the other from 1000 to 10000 in javascript, I look to minify the code if possible.
I am beginner in coding , but all kinds of methods are acceptable for me.
I am updating the code as previous code might get ambigous for some users.
function dummyFunc(x,y){
if( some logic for x == some logic for y){
return true;
}
return false;
}
for(var i = 1000;i < 10000;i++){
for(var j = 1000;j < 10000;j++){
if(dummyFunc(i,j)){
for(var k = 1000;k < 10000;k++){
if(dummyFunc(j,k)){
for(var l = 1000;l < 10000;l++){
if(dummyFunc(k,l)){
for(var m = 1000;m < 10000;m++){
if(dummyFunc(l,m)){
for(var n = 1000;n < 10000;n++){
if(dummyFunc(m,n)){
break;
}
}
}
}
}
}
}
}
}
}
}
You could extract the for loop into a function:
function range(start, end, callback) {
for(let i = start, start < end, i++)
callback(i);
}
That can be used as:
range(1000, 10000, i => {
range(1000, 10000, j => {
range(1000, 10000, k => {
range(1000, 10000, l => {
range(1000, 10000, m => {
range(1000, 10000, n => {
console.log(i, j, k, l, m, n);
});
});
});
});
});
To simplify that even further, you could use a generator that yields an array of values which you can destructured:
function* ranges(start, end, repeats) {
if(repeats > 1) {
for(const values of ranges(start, end, repeats - 1)) {
for(const value of ranges(start, end, 0)) {
yield values.concat(value);
}
}
} else {
for(let i = start; i < end; i++)
yield [i];
}
}
That can be used as:
for(const [i, j, k, l, m, n] of ranges(1000, 10000, 6)) {
console.log(i, j, k, l, m, n);
}
Use the following code. Instead of returning true or false, you should start the loop inside the dummyFunc, this will call the function recursively.
function dummyFunc(x,y){
if( some logic for x == some logic for y)
for(var i = 1000;i < 10000;i++)
for(var j = 1000;j < 10000;j++)
dummyFunc(i,j);
}
for(var i = 1000;i < 10000;i++)
for(var j = 1000;j < 10000;j++)
dummyFunc(i,j);
To add clarity to how recursive function is working (writing in usual recursive function style - return is some condition is satisfied else call function again) you may write it as follows
function dummyFunc(x,y){
if( !(some logic for x == some logic for y))
return;
for(var i = 1000;i < 10000;i++)
for(var j = 1000;j < 10000;j++)
dummyFunc(i,j);
}
for(var i = 1000;i < 10000;i++)
for(var j = 1000;j < 10000;j++)
dummyFunc(i,j);
Here is your complete simplified code
function getDeeper(i, j, start, end, level) {
if(j === end && (i = (i+1)) && (j = start)) {}
if(dummyFunc(i, j)) {
if(level === 4) return;
getDeeper(j, start, start, end, ++level);
}
getDeeper(i, ++j, start, end, level);
}
getDeeper(1000, 1000, 1000, 10000, 0);
Since your question is How do I minify the following series of for loops into a less compact code? and is not specifically asking for a better code example I will instead show you how to fish instead of giving you the fish.
You need to read about Structured program theorem:
It states that a class of control flow graphs (historically called
charts in this context) can compute any computable function if it
combines subprograms in only three specific ways (control structures).
These are
Executing one subprogram, and then another subprogram (sequence)
Executing one of two subprograms according to the value of a boolean expression (selection)
Repeatedly executing a subprogram as long as a boolean expression is true (iteration)
Also worth reading is Flow Diagrams, Turing Machines And Languages With Only Two Formation Rules by Corrado Bohm and Giuseppe Jacopini for whom the theorem is named after.
So if I understand you code correctly then while the example looks like a long task when computed as such
If we assume that your browser makes 1000 * 1000 iterations per
second, this will take 1000 * 1000 * 1000 * 1000 seconds to complete.
That is very long (317 centuries)
as noted by Jonas Wilms,
from my extensive experience with predicates which is that a predicate will only return true or false and if you know that once it is true you can stop processing because you have the result. On the other hand if the result is false then you need to process all of the results. The real trick is not to brute force through all of the values but to quickly eliminate combinations of inputs that don't help lead to a solution.
While I don't know exactly what you are trying to do, I would also take a look at Binary decision diagram and/or Constraint satisfaction which are great ways to simplify complex problems.
Based on the code you provided, you could simplify with the following:
function dummyFunc(x,y){
if( some logic for x == some logic for y){
return true;
}
return false;
}
for(var i = 1000;i < 10000;i++) {
for(var j = 1000;j < 10000;j++) {
if(dummyFunc(i,j)) {
break;
}
}
}
You could argue I am taking your example too literally, but hopefully it either answers your question or illustrates why you need a better example to get a better answer.
the same for loop repeated each time
so you can move it inside the function
function dummyFunc(x) {
for (y = 1000; y < 10000; y++) {
if (some logic for x == some logic for y) {
return y;
}
}
return false;
}
for (var i = 1000; i < 10000; i++) {
if (j = dummyFunc(i)) {
if (k = dummyFunc(j)) {
if (l = dummyFunc(k)) {
if (m = dummyFunc(l)) {
if (n = dummyFunc(m)) {
break;
}
}
}
}
}
}
this solution if you need i,j,k,l,m and n variables
if you don't need those variables
by using recursive function
this solution gives the equal to n variable in your question
function dummyFunc(x) {
numberOfLoops = 6;
for (y = 1000; y < 10000; y++) {
if (some logic forr x == some logic forr y) {
if (numberOfLoops == 0) {
return n;
}
n=dummyFunc(x)
numberOfLoops--;
}
}
return false;
}
for (var i = 1000; i < 10000; i++) {
n = dummyFunc(i)
}
I haven't check my code , your comments would be helpful
I'm still not sure why exactly you would want to do this, but below are two potential solutions depending on the behavior you want at the "break". Neither is very pretty, but they both work for the problem described.
Solution (A) - breaks only the inner-most loop, which would exactly match the behavior described in the question.
function loopenstein(minn, maxx, maxDepth, dummyCall) {
function recursiveLoop(i, minn, maxx, depth, dummyCall) {
for (var j = minn; j < maxx; j++)
if (dummyFunc(i, j)) {
if (depth <= 0) {
console.log("break me daddy...")
return;
}
recursiveLoop(j, minn, maxx, depth - 1, dummyCall))
}
}
for (var i = minn; i < maxx; i++) {
recursiveLoop(i, minn, maxx, maxDepth, dummyCall))
}
}
/* usage */
loopenstein(1000, 10000, 6, dummyFunc)
Solution (B) - breaks completely out of all loops once dummyFunc returns true for the 6th loop.
function loopenstein(minn, maxx, maxDepth, dummyCall) {
//recursive helper function
function loopensteinHelper(i, minn, maxx, depth, dummyCall) {
for (var j = minn; j < maxx; j++)
if (dummyFunc(i, j)) {
if (depth <= 0) {
console.log("break me daddy...")
return true;
} else if (loopensteinHelper(j, minn, maxx, depth - 1, dummyCall)) {
return true;
}
return false;
}
for (var i = minn; i < maxx; i++) {
if (loopensteinHelper(i, minn, maxx, maxDepth, dummyCall)) {
return true;
}
}
return false;
}
/* usage */
var isFound = loopenstein(1000, 10000, 6, dummyFunc)
I would recommend to use promise. Below is the modified version of the code:
var looper = function(start = 1000, end = 10000) {
return new Promise(function(resolve, reject) {
console.log('Starting');
for (; start <= end; start++) {
// Your computation goes here
}
resolve();
});
}
Now if you want to run a loop 3 times use it like:
looper().then(looper()).then(looper())
If you need to run loop 4 times use as:
looper().then(looper()).then(looper()).then(looper())
You can learn more about Promises here
for(var i = 0; i <= 10; i+1){
console.log(i); // the loop goes on and on
}
why this for loop don't stop ? I did specifically typed in condition that it need to stop on 10.
The i+1 is your issue. It should be i = i + 1, i++ or i+=1
These are just different ways of adding 1 to the current value of i
for(var i = 0; i <= 10; i++){
console.log(i);
}
You never change i.
for (var i = 0; i <= 10; i + 1) {
^^^^^
You need to increment i
for (var i = 0; i <= 10; i++) { // or
for (var i = 0; i <= 10; i = i + 1) {
It's missing the =: i is not being mutated:
for(var i = 0; i <= 10; i += 1){
console.log(i); // the loop goes on and on
}
Change it to i++ and it will work. Right now you are just checking against 0+1 every loop iteration, and that will never be > 10.
I have an issue with a recursive algorithm, that solves the problem of finding the happy numbers.
Here is the code:
function TestingFunction(number){
sumNumberContainer = new Array(0);
CheckIfNumberIsHappy(number);
}
function CheckIfNumberIsHappy(number){
var sumOfTheNumbers = 0;
for (var i = 0; i < number.length; i++) {
sumOfTheNumbers += Math.pow(parseInt(number[i]), 2);
}
console.log(sumOfTheNumbers);
if(sumOfTheNumbers == 1){
return CheckIfNumberIsHappy(sumOfTheNumbers.toString());
//return true;
} else {
sumNumberContainer.push(sumOfTheNumbers);
if(sumNumberContainer.length > 1){
for (var i = 0; i < sumNumberContainer.length - 1; i++) {
for (var j = i + 1; j < sumNumberContainer.length; j++) {
if(sumNumberContainer[i] == sumNumberContainer[j]){
return CheckIfNumberIsHappy(sumOfTheNumbers.toString());
//return false;
}
}
}
}
CheckIfNumberIsHappy(sumOfTheNumbers.toString());
}
}
Algorithm is working ALMOST fine. I've tested it out by calling function with different numbers, and console was displaying correct results. The problem is that I almost can't get any value from the function. There are only few cases in which I can get any value: If the number is build out of ,,0", and ,,1", for example 1000.
Because of that, I figured out, that I have problem with returning any value when the function is calling itself again.
Now I ended up with 2 results:
Returning the
return CheckIfNumberIsHappy(sumOfTheNumbers.toString());
which is giving an infinity looped number. For example when the number is happy, the function is printing in the console number one again and again...
Returning the
//return true
or
//return false
which gives me an undefined value
I'm a little bit in check by this problem, and I'm begging you guys for help.
I would take a step back and reexamine your problem with recursion in mind. The first thing you should think about with recursion is your edge cases — when can you just return a value without recursing. For happy numbers, that's the easy case where the sum of squares === 1 and the harder case where there's a cycle. So test for those and return appropriately. Only after that do you need to recurse. It can then be pretty simple:
function sumSq(num) {
/* simple helper for sums of squares */
return num.toString().split('').reduce((a, c) => c * c + a, 0)
}
function isHappy(n, seen = []) {
/* seen array keeps track of previous values so we can detect cycle */
let ss = sumSq(n)
// two edge cases -- just return
if (ss === 1) return true
if (seen.includes(ss)) return false
// not an edge case, save the value to seen, and recurse.
seen.push(ss)
return isHappy(ss, seen)
}
console.log(isHappy(23))
console.log(isHappy(22))
console.log(isHappy(7839))
Here's a simplified approach to the problem
const digits = x =>
x < 10
? [ x ]
: [ ...digits (x / 10 >> 0), x % 10 ]
const sumSquares = xs =>
xs.reduce ((acc, x) => acc + x * x, 0)
const isHappy = (x, seen = new Set) =>
x === 1
? true
: seen.has (x)
? false
: isHappy ( sumSquares (digits (x))
, seen.add (x)
)
for (let n = 1; n < 100; n = n + 1)
if (isHappy (n))
console.log ("happy", n)
// happy 1
// happy 7
// happy 10
// ...
// happy 97
The program above could be improved by using a technique called memoization
Your code is almost correct. You just forgot to return the result of the recursive call:
function TestingFunction(number){
sumNumberContainer = new Array(0);
if (CheckIfNumberIsHappy(number))
console.log(number);
}
function CheckIfNumberIsHappy(number){
var sumOfTheNumbers = 0;
for (var i = 0; i < number.length; i++) {
sumOfTheNumbers += Math.pow(parseInt(number[i]), 2);
}
if(sumOfTheNumbers == 1){
return true;
} else {
sumNumberContainer.push(sumOfTheNumbers);
if(sumNumberContainer.length > 1){
for (var i = 0; i < sumNumberContainer.length - 1; i++) {
for (var j = i + 1; j < sumNumberContainer.length; j++) {
if(sumNumberContainer[i] == sumNumberContainer[j]){
return false;
}
}
}
}
return CheckIfNumberIsHappy(sumOfTheNumbers.toString());
}
}
for (let i=0; i<100; ++i)
TestingFunction(i.toString()); // 1 7 10 13 ... 91 94 97
I've got the solution, which was given to me in the comments, by the user: Mark_M.
I just had to use my previous
return true / return false
also I had to return the recursive statement in the function, and return the value of the CheckIfTheNumberIsHappy function, which was called in TestingFunction.
The working code:
function TestingFunction(number){
sumNumberContainer = new Array(0);
return CheckIfNumberIsHappy(number);
}
function CheckIfNumberIsHappy(number){
var sumOfTheNumbers = 0;
for (var i = 0; i < number.length; i++) {
sumOfTheNumbers += Math.pow(parseInt(number[i]), 2);
}
console.log(sumOfTheNumbers);
if(sumOfTheNumbers == 1){
return true;
} else {
sumNumberContainer.push(sumOfTheNumbers);
if(sumNumberContainer.length > 1){
for (var i = 0; i < sumNumberContainer.length - 1; i++) {
for (var j = i + 1; j < sumNumberContainer.length; j++) {
if(sumNumberContainer[i] == sumNumberContainer[j]){
return false;
}
}
}
}
return CheckIfNumberIsHappy(sumOfTheNumbers.toString());
}
}
Thanks for the great support :)
please take a quick look at this function that I have found on the web.
function longestCommonSubstring(string1, string2){
// init max value
var longestCommonSubstring = 0;
// init 2D array with 0
var table = Array(string1.length);
for(a = 0; a <= string1.length; a++){
table[a] = Array(string2.length);
for(b = 0; b <= string2.length; b++){
table[a][b] = 0;
}
}
// fill table
for(var i = 0; i < string1.length; i++){
for(var j = 0; j < string2.length; j++){
if(string1[i]==string2[j]){
if(table[i][j] == 0){
table[i+1][j+1] = 1;
} else {
table[i+1][j+1] = table[i][j] + 1;
}
if(table[i+1][j+1] > longestCommonSubstring){
longestCommonSubstring = table[i+1][j+1];
}
} else {
table[i+1][j+1] = 0;
}
}
}
return longestCommonSubstring;
}
It returns the length of the longest common substring as an int. Now to my question, is it possible to modify this function, so that it returns the actual string instead of just returning the length of the substring, I'm quite new at programming and thought that just modifying this secetion would help if(string1[i]==string2[j]){ push(string1[i]}, but it isn't that easy, because I don't want every single character that is the same in those 2 strings to be added in that array, only those that are exactly the same.
Thanks in advance =)
Well for minimal changes to the existing function you could declare a new variable:
var theCommonString = '';
Then in the middle of the function add a line after this existing one:
longestCommonSubstring = table[i+1][j+1];
that says something like:
theCommonString = string1.substr(i + 1 - longestCommonSubstring,
longestCommonSubstring);
(That i + 1 index may be a little off, I haven't bothered working it out carefully.)
Then at the end just return your new variable instead of the existing one.
Note that if there is more than one common sub string of the same length this will return the last one.
You can just store the whole common substring in the table instead of its length:
function longestCommonSubstring(string1, string2){
// init max value
var longestCommonSubstring = "";
// init 2D array with 0
var table = Array(string1.length);
for(a = 0; a <= string1.length; a++){
table[a] = Array(string2.length);
for(b = 0; b <= string2.length; b++){
table[a][b] = 0;
}
}
// fill table
for(var i = 0; i < string1.length; i++){
for(var j = 0; j < string2.length; j++){
if(string1[i]==string2[j]){
if(table[i][j] == 0){
table[i+1][j+1] = string1[i];
} else {
table[i+1][j+1] = table[i][j] + string1[i];
}
if(table[i+1][j+1].length > longestCommonSubstring.length){
longestCommonSubstring = table[i+1][j+1];
}
} else {
table[i+1][j+1] = 0;
}
}
}
return longestCommonSubstring;
}