Variable for loop - javascript

If reverse == true I want to run one kind of loop, else I want to run another one.
Currently, here is an example of my code:
if (reverse) {
for (var i = 0; i < length; i++) {
...
}
} else {
for (var i = length; i >= 0; i--) {
...
}
}
The code inside is really big, and is quite the same. I could use a function, but this function would have so many params that is not a good choice.
So I've tried to do something like that:
var loopStart1 = startPageIndex;
if (!reverse) {
condition1 = function(i) {
return i < length;
}
increment1 = function(i) {
return ++i;
}
} else {
condition1 = function(i) {
return i >= 0;
}
increment1 = function(i) {
return i--;
}
}
mainLoop: for (var i = loopStart1; condition1(i); increment1(i)) {
But now I have an infinite loop.
Any idea on how to solve this issue?

Why not do it inline?! ;)
var start = startPageIndex;
for (var i = start; (reverse && i >= 0) || (!reverse && i < length); reverse ? --i : ++i) { }

Assuming the specific case is to traverse through an array either backwards or forwards, there are two simple ways to do that.
1) Just conditionally reverse the array before looping, or
2) Use a single loop but conditionally map the loop variable to something else. Something like...
for (var i = 0; i < length; i++) {
var j = i;
if (reverse) {
j = length - (i + 1);
}
// ...then use j instead of i
}

If you want to make it dynamic, I wouldn't use a for loop, but a do..while loop to be easier to customize.
Your main function would just have a simple reverse bool flag and you could just pass that.
Inside that function that you want to depend on the reverse flag, you can use the ternary expression in the condition (x ? y : z)
Makes it clearer to read. In theory you can do it in a for loop directly, using two ternary directions.
do {
//Your actions here, using i as counter
//Here you will do the counter direction change
if(reverse)
i--;
else
i++;
// Use ternary expression if (x ? y : z) in the while condition
// Reads as: If x is true, do y, else do z
// All in one line
} while(reverse ? i>=0 : i<n);
Ideally, in these situations I would consider using iterators.

How about a simple loop function,
Below I've created a simple function called myLoop, were you can pass the length, if it's reversed, and what callback you want for each loop iteration.
eg.
function myLoop(length, reverse, each) {
var index;
if (!reverse) {
for (index = 0; index < length; index ++) each(index);
} else {
for (index = length -1; index >= 0; index --) each(index);
}
}
function show(index) {
console.log("Loop index = " + index);
}
console.log("forward");
myLoop(5, false, show); //normal
console.log("revere");
myLoop(5, true, show); //reversed

I would opt for the same code, just change the array.
var array = ['one', 'two', 'three'];
var reversed = true;
let arrToUse = reversed ? array.reverse() : array;
for (var i = 0; i < arrToUse.length; i++) {
console.log(arrToUse[i]);
}

Check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions
Primitive parameters (such as a number) are passed to functions by value; the value is passed to the function, but if the function changes the value of the parameter, this change is not reflected globally or in the calling function.
This is what happens when you call increment1(i), outer i in for loop is not changed. To fix it just set i from increment1(i)'s return value.
mainLoop: for (var i = loopStart1; condition1(i); i = increment1(i)) {

Related

If condition vs loop one item

I was just curious, is it worth to have if condition before looping some array, that in 90% will be array of 1 item?
Code example:
const a = [3];
const aLength = a.length;
if(aLength > 1) {
for(let i = 0; i < aLength; i++) {
func(i);
}
} else {
func();
}
function func(position = 0) {
console.log('hi' + position);
}
I agree with Federico's comment, a single for loop is the most readable in this case.
Also, even though you reuse it, there is not much point to extracting a.length into aLength
const a = [3];
for(let i = 0; i < a.length; i++) {
func(i);
}
function func(position) {
console.log('hi' + position);
}
Warning: very personal perspective down there, you could achieve the same level of clarity with comments too.
Well, unless the single element case has a very specific meaning in your domain. In which case, I would separate them with two functions with very specific names as follows:
const a = [3];
if(a.length > 1) {
handleMultiple(a);
} else {
handleSingleAndWhyItIsASpecialCase(a)
}
handleMultiple(array) {
for(let i = 0; i < array.length; i++) {
func(i);
}
}
handleSingleAndWhyItIsASpecialCase(array) {
func();
}
function func(position = 0) {
console.log('hi' + position);
}
As Hamid said below, you can easily turn it into a oneliner:
[45,63,77].forEach((element, index) => console.log(index));
Consider using forEach instead of map to make your intent clear though.
Write clean code and make everyone happy.
you can eliminate if and loop:
const a=[5,6,3]
a.forEach((value,index)=>console.log('hi'+index));

Codewars - Delete occurrences of an element if it occurs more than n times

I just completed this challenge on Codewars.
I have completed the question with my own method, but can someone explain to me the Best Practice answer. Can somehow explain how this code functions?
function deleteNth(arr,x) {
var cache = {};
return arr.filter(function(n) {
cache[n] = (cache[n]||0) + 1;
return cache[n] <= x;
});
}
I did this:
function deleteNth(arr,n){
var count = 0;
//loop backwards so it removes duplicates from the right
for(let i= arr.length; i > 0; i--){
for(let j=0; j < arr.length; j++){
if (arr[i] == arr[j]){
count += 1
}
}
if(count > n){
arr.splice(i,1);
i = arr.length;
}
count = 0;
}
return arr;
}
This is how the first code works. It is a quite nice solution and quite easy to read if you understand the filter function.
function deleteNth(arr, x) {
// Create an empty object to store how many times each object exists
var cache = {};
// Call the filter function, the delegate is called once for each item
// in the array and you return true or false depending on if it should
// be kept or not
return arr.filter(function(n) {
// Use the item as key and store the number of times the item has appeared.
// (cache[n]||0) fetches the current value of cache[n] or zero if it doesn't
// exist. Then add one to it and store it.
cache[n] =(cache[n]||0) + 1;
// If the number of times it has appeared in the array is less or equal to
// the limit then return true so the filter function keeps it.
return cache[n] <= x;
});
}

Why is this function creating an infinite looop?

The first function determines if a number is prime. The second function is supposed to create an array with all prime numbers up to and including the max value, but it gives me an infinite loop for some reason.
function isPrime(num) {
for (i = 2; i < num; i++) {
if (num % i === 0) {
return false
}
}
if (num <= 1) {
return false;
}
return true;
}
function primes(max) {
var all = [];
for (i = 2; i <= max; i++) {
if (isPrime(i)) {
all.push(i);
}
}
}
primes(17);
Your i variable is global, so both functions use the same i. This means the first function changes it while the second one is looping.
As the first function will have set i to num-1 when it finishes, and num was the value of i before executing it, it effectively decrements i with one. And so i will get the same value in the next iteration of the loop in the second function, never getting forward.
Solve this by putting the var keyword in both functions.
for(var i=2; // ...etc)
The variable i in your two loops are global variables and they overwrite each other so the first loop never ends.
In ur code problem is with the scope of variable i, In prime no calculation, u can check upto the sqrt of no, if no is not divisible by any no upto its sqrt, then it will a prime no, Try this:
function isPrime(num) {
let k = Math.sqrt(num);
for (let i = 2; i <= k; i++) {
if (num % i === 0) {
return false
}
}
return true;
}
function primes(max) {
let all = [];
for (let i = 2; i <= max; i++) {
if (isPrime(i)) {
all.push(i);
}
}
console.log(all)
}
primes(17);
primes(25);

How to do Nested For Loops in Functional Style

I'm in the process of learning functional programming, and completely getting rid of for loops has been a challenge sometimes, because they provide so much control and freedom. Below is an example of checking if a string is an isogram or not (no letters should be repeated). With nested for loops, it became an easy solution. Is there a way to do this the functional way with any high order functions or anything else? Any suggestion would be a huge help.
Code:
function isIsogram(string) {
let array = string.split('');
let condition = true;
for (let i = 0; i < string.length; i++) { //first loop picks character
for (j = i + 1; j < string.length; j++) { //second loop compares it rest
if (array[i].toLowerCase() == array[j].toLowerCase())
condition = false; //if repeat, the condition false
}
return condition;
}
}
You can use every or some together with a suitable string function:
function isIsogram(string) {
string = string.toLowerCase(); // case insensitive
return string.split('').every(function(character, index) {
return !string.includes(character, index+1);
});
}
Instead of includes you might also have utilised indexOf.
You can sort the String first and then apply every on it. It will stop the iteration as soon as two successive letters are the same:
Here is an improved implementation. Credit goes to #Xotic750:
function isIsogram(x) {
return Array.from(x.toLowerCase()).sort().every((y, i, xs) => i === 0
? true
: y !== xs[i - 1]);
}
console.log( isIsogram("consumptively") );
console.log( isIsogram("javascript") );
The implementation uses Array.prototype.every's second parameter, which represents the index of the current element (of the iteration). Please note that isIsogram solely depends on functions and their arguments.
Another example, like #Bergi but using some ES6 features for comparison.
function isIsogram(string) {
string = string.toLowerCase(); // case insensitive
for (let character of Array.from(string).entries()) {
if (string.includes(character[1], character[0] + 1)) {
return false;
}
}
return true;
}
console.log(isIsogram('abc'));
console.log(isIsogram('abca'));
How your ES3 style code could have looked (noting some of the issues pointed out in the comments)
function isIsogram(string) {
string = string.toLowerCase(); // case insensitive
var length = string.length;
for (var i = 0; i < length; i += 1) {
for (var j = i + 1; j < length; j += 1) {
if (string.charAt(i) === string.charAt(j)) {
return false;
}
}
}
return true;
}
console.log(isIsogram('abc'));
console.log(isIsogram('abca'));

How can I search for a value in an array and delete just one of those values if 2 or more is found?

I have an array
var aos = ["a","a","a","b","b","c","d","d"];
I want to know if I can remove just 1 item if it finds 2 or more of the same value in the array. So for instance if it finds
"a", "a"
it will remove one of those "a"
This is my current code:
var intDennis = 1;
for (var i = 0; i < aos.length; i++) {
while (aos[i] == aos[intDennis]) {
aos.splice(i, 1);
intDennis++;
console.log(aos[intDennis], aos[i]);
}
intDennis = 1;
}
NOTE: My array is sorted.
Edited after better understanding of OP use-case.
Updated solution and fiddle test to incorporate suggestion from pst in comments.
(Not for nothing, but this method does not require the original array be sorted.)
Try this...
var elements = [];
var temp = {};
for (i=0; i<aos.length; i++) {
temp[aos[i]] = (temp[aos[i]] || 0) + 1;
}
for (var x in temp) {
elements.push(x);
for (i=0; i<temp[x]-2; i++) {
elements.push(x);
}
}
Fiddle Test
Because you said you have a sorted array, you only need to remove the second time a element is found. You only need one for.
The splice() function returns the removed element so, just use it to not remove more elements of that kind.
This solution is more clean and efficient.
var aos = ["a","a","a","b","b","c","d","d"];
var lastRemoved = "";
for (var i = 1; i < aos.length; i++) {
if (aos[(i-1)] == aos[i] && lastRemoved != aos[i]) {
lastRemoved = aos.splice(i, 1);
}
}
Code tested and working. Result: ["a", "a", "b", "c", "d"]
I don't believe there's any better way to do this on an unsorted array than an approach with O(n^2) behaviour. Given ES5 array-builtins (supported in all modern browsers, though not in IE prior to IE9), the following works:
aos.filter(function(value, index, obj) { return obj.indexOf(value) === index; })
UPDATED ANSWER TO REMOVE ONLY 1 DUPLICATE:
Assuming that each object will resolve to a unique String, here's a potential solution. The first time the object is detected, it sets a counter for that object to one. If it finds that object again, it splices that element out and increments the associated counter. If it finds that element more times, it will leave it alone.
var elements = {};
for (var i = 0; i < aos.length; i++) {
if(elements[aos[i]]){
if(elements[aos[i]] == 1){
aos.splice(i,1);//splice the element out of the array
i--;//Decrement the counter to account for the reduced array
elements[aos[i]]++;//Increment the count for the object
}
} else {
elements[aos[i]] = 1;//Initialize the count for this object to 1;
}
}
Here's the test fiddle for this.
I would not mutate the input -- that is, don't use splice. This will simplify the problem a good deal. Using a new array object here may actually be more efficient. This approach utilizes the fact that the input array is sorted.
Consider: (jsfiddle demo)
var input = ["a","a","a","b","b","c","d","d"]
var result = []
for (var i = 0; i < input.length; i++) {
var elm = input[i]
if (input[i+1] === elm) {
// skip first element (we know next is dup.)
var j = i + 1
for (; input[j] === elm && j < input.length; j++) {
result.push(input[j])
}
i = j - 1
} else {
result.push(elm)
}
}
alert(result) // a,a,b,c,d
Happy coding.
Replace === with a custom equality, as desired. Note that it is the first item is omitted from the output, which may not always be "correct".
REVISED EXAMPLE
function removeDuplicate(arr) {
var i = 1;
while(i < arr.length) {
if(arr[i] == arr[i - 1]) {
arr.splice(i, 1);
}
while(arr[i] == arr[i - 1] && i < arr.length) {
i += 1;
}
i += 1;
}
return arr;
}
alert(removeDuplicate(["a","a","a","b","b","c","d","d"]));

Categories