understanding what the code exactly does behind the scenes - javascript

I don't know if this question is really asked but i have a running code with for loop which sorts an array of numbers. But i don't get the idea behind the code. If someone who has experience can tell me whats happening behind the scenes would be great.
Here is the code:
var a = [1, 7, 2, 8, 3, 4, 5, 0, 9];
for(i=1; i<a.length; i++)
for(k=0; k<i; k++)
if(a[i]<a[k]){
var y = a[i]
a[i]= a[k]
a[k]=y;
}
alert(a);

First, having your code indented properly and not taking advantage of optional syntax (braces and semi-colons) will go a long way towards your understanding of how the code is processed. Technically, curly braces are not required with for and if statements if there is just one statement to perform within the loop or within the branch of the if. Additionally, JavaScript technically does not require that you place a semicolon at the end of your statements. Do not take advantage of either one of these optional syntaxes as it will only make things more confusing and possibly lead to bugs in your code.
With that in mind, your code really should be written as follows. The job of this code is to sort the items in the array. It does this by looping through the array and always checking the current array item and the item that precedes it. If the items are out of order, the values are swapped.
Please see the comments for descriptions of what each line does:
// Declare and populate an array of numbers
var a = [1, 7, 2, 8, 3, 4, 5, 0, 9];
// Loop the same amount of times as there are elements in the array
// Although this code will loop the right amount of times, generally
// loop counters will start at 0 and go as long as the loop counter
// is less than the array.length because array indexes start from 0.
for(i=1; i<a.length; i++){
// Set up a nested loop that will go as long as the nested counter
// is less than the main loop counter. This nested loop counter
// will always be one less than the main loop counter
for(k=0; k<i; k++){
// Check the array item being iterated to see if it is less than
// the array element that is just prior to it
if(a[i]<a[k]){
// ********* The following 3 lines cause the array item being iterated
// and the item that precedes it to swap values
// Create a temporary variable that stores the array item that
// the main loop is currently iterating
var y = a[i];
// Make the current array item take on the value of the one that precedes it
a[i]= a[k];
// Make the array item that precedes the one being iterated have the value
// of the temporary variable.
a[k]=y;
}
}
}
alert(a);

Related

Javascript Array Diff

I am working on the CodeWars kata Array.diff:
Your goal in this kata is to implement a difference function, which subtracts one list from another and returns the result.
It should remove all values from list a, which are present in list b keeping their order.
arrayDiff([1,2],[1]) == [2]
If a value is present in b, all of its occurrences must be removed from the other:
arrayDiff([1,2,2,2,3],[2]) == [1,3]
My code:
function arrayDiff(a, b) {
for(let i = 0; i<b.length; i++){
for(let j = 0; j<a.length; j++){
if(b[i]===a[j]){
a.splice(j,1)
}
}
}
return a;
}
Somehow, although it works for most arrays, it doesn't give correct results for some arrays. Where exactly am I going wrong?
Using splice on an array which you are iterating is tricky. By removing the current element from that array, the elements that came after it now appear at an index that is one less than where they were before, yet the loop variable will still be increased for the next iteration.
Moreover, this algorithm has a bad time complexity with its double loop over the whole range of indices.
You can do this in linear time: first create a Set so that it only retains unique values as they appear in the second array, and then filter the first array for values that don't appear in that set.
This idea is used in this spoiler:
function array_diff(a, b) {
const remove = new Set(b);
return a.filter( k => !remove.has(k) );
}

Which way of iterating array and changing element by map is more efficient?

I have a question about map.As you know by using map we can iterate arrays and able to change the element of arrays.To do that there are two ways and I listed them below.Which way has less complexity and more performance?(In the examples arrays are not big but think about the real world like 500 elements in an array)
var numbers = [4, 9, 16, 25];
numbers=numbers.map(e=>{
return e=e*2;
})
In this approach assigning each returned value to current array.
numbers.map((e,a)=>{
numbers[a]=e*2;
})
In this one on each iteration we need to go to array by index to find element and I think this is worse than above but Im not sure.
Don't use map if you're not going to use the array it creates. If you just want to loop through an array, use a loop or forEach or similar.
Whether you want a new array or not, the most efficient way to loop through an array is usually a for loop:
const numbers = [4, 9, 16, 25];
for (let i = 0, len = numbers.length; i < len; ++i) {
numbers[i] = numbers[i] * 2;
}
(Or any of several variations on that.)
But "most efficient" is extremely unlikely to actually matter in real-world code, so using things like map (if you want a new array) or forEach (if you don't) is just fine. map is idiomatic for that operation (creating a new array containing the entries from a previous array modified in some way).
Conclusion:
If you want a new array, feel free to use map, or create a blank array and use any of your options for looping arrays to loop the original and add entries to the new one.
If you don't want a new array, don't use map, use any of your options for iterating over the original except for ones that don't give you the index, and assign the updated value back to the entry.
Semantically you need for or forEach when you do not need a new array.
For sufficiently small arrays there is no difference in performance
var numbers = [4, 9, 16, 25];
numbers.forEach((e,i)=> numbers[i] = e*2);
console.log(numbers)
nums.forEach((v, i) => nums[i] = v);
is the fastest method possible that you listed (equivalent to your second method. Why? nums = nums.map(...) creates an entire copy of the array in memory and replaces the array with that copy. When you simply iterate over the array with an index and replace elements, you avoid making that copy, and it ends up being around 15% faster (see this benchmark.
The fastest way to change a value of an array is to iterate the index and update the item.
This approach is faset than forEach or map, because it does not have the overhead of this and it does not need the feature of the supplied structure of the callback.
var numbers = [4, 9, 16, 25];
for (let i = 0; i < numbers.length; i++) numbers[i] *= 2;
console.log(numbers);

Reduce the complexity of matching the elements of two arrays

I wrote a code that extracts column headers (the first row in a sheet) from a google sheet and compares them with an array of objects. Each object in the objects array has 3 properties: "question", "answer", and "category". The code compares the header of each column, with the "question" property pf each object in the array.
If they're similar it should add the index of the column as a key to some dictionary and set its value to be an array that holds the answer and the category of that question. No need to much explain why I'm doing this, but briefly I built this logic to be able to grade applicants answers on some questions (hence linking the index of a question to its right answer and to its category). Here is the code:
for (i = 0; i<columnHeaders[0].length; i++){
for (j=0; j<questionsObjects.length; j++){
//Get the current question and convert it to lower case
question = questionsObjects[j].question.toString().toLowerCase();
//Get column header, remove any spaces and new lines from it, and convert it to lower case
columnHeader = columnHeaders[0][i].toString().toLowerCase();
if (isStringSimilar(columnHeader, question)){
//Link the column index to its corresponding question object
var catAndAnswer = [];
catAndAnswer.push (questionsObjects[j].category.toLowerCase());
catAndAnswer.push (questionsObjects[j].rightAnswer.toLowerCase());
columnsQuestionsDictionary[i] = catAndAnswer;
} else {
SpreadsheetApp.getActive().getSheetByName("log").appendRow(["no", columnHeader, question]);
}
}
}
The code runs well, my only problem is complexity, it's very high. In some cases this method takes almost 6 minutes to execute (for this case I had around 40 columns and 7 question objects)! To decouple the nested loops, I thought of concatenating the questions values (of all objects in the questions object array) into 1 single string where I precede each question with its index in the objects array.
For example:
var str = "";
for (j=0; j<questionsObjects.length; j++){
str = str + j + questionsObjects[j].question.toString.toLowerCase();
}
Then, I can have another separate loop through the columns headers, extract each header into a string, then use regex exec method to match that header in the long questions string (str), and if it's found I would get its index in str, then subtract 1 from it to know its index in the objects array. However, it turned out that the complexity of matching a regular expression is O(N) where N is the length of the string we search in (str in this example), given that this will be inside the columns loop, I see that we still get a high complexity that can go to O(N^2).
How can I optimize those nested loops so the code runs in the most efficient way possible?
OK, so I used the way suggested by Nina Schholz in the comments and I moved columnHeader = columnHeaders[0][i].toString().toLowerCase(); to be in the outer loop instead of being in the inner one since it's only needed in the outer one.
The time needed to run the code was reduced from ~295 seconds to ~208 seconds, which is good.
I also tried switching the loops order where I made the outer loop to be the inner one and the inner one to be the outer one and updated the usage of i and j accordingly. I did that because it's always recommended to have the outer loop with less iterations and the inner one with more iterations (according to this resource), and in my case, the loop that iterates over questions object array is always expected to have number of iterations <= the other loop.
This is because if we want to calculate the complexity of 2 nested loops, it'll be (ixj) + i, where i and j represents the number of iterations of the outer and the inner loops, respectively. Switching the loops order won't impact the multiplication part (ixj) but it'll impact the addition part. So, it's always better to have the outer number of iterations smaller than the inner ones.
After doing this the final time of the run became ~202 seconds.
Of course since the loops are switched now, I moved this line to the inner loop: columnHeader = columnHeaders[0][i].toString().toLowerCase();, but at the same time I moved this question = questionsObjects[j].question.toString().toLowerCase(); to be under the outer loop because it's only needed there.

Question concerning efficiency in calculated array indices and repeated references to a specific array value

In trying to minimize the time it takes a JavaScript function to process, please consider this set up. In the function is a loop that operates on an array of similar objects. The index of the array is [4 + loop counter] and there are several references to array[4+i][various property names], such as a[4+i].x, a[4+i].y, a[4+i].z in each loop iteration.
Is it okay to keep calculating 4+i several times within each loop iteration, or would efficiency be gained by declaring a variable at the top of the loop to hold the value of 4+i and use that variable as the index, or declare a variable to be a reference to the a[4+i] object? Is it more work for the browser to declare a new variable or to add 4+i ten times? Does the browser work each time to find a[n] such that, if one needs to use the object in a[n] multiple times per loop iteration, it would be better to set x = a[n] and just reference x.property_names ten times?
Thank you for considering my very novice question.
Does the browser work each time to find a[n] such that, if one needs to use the object in a[n] multiple times per loop iteration, it would be better to set x = a[n] and just reference x.property_names ten times?
Yes. Although the JavaScript engine may be able to optimize away the repeated a[4+i] work, it also might not be able to, depending on what your code is doing. In contrast, creating a local variable to store the reference in is very, very little work.
Subjectively, it's also probably clearer to the reader and more maintainable to do x = a[4+i] once and then use x.
That said, the best way to know the answer to this question in your specific situation is to do it and see if there's an improvement.
This snippet runs for a bit more than half minutes...
function m(f){
const t=[Date.now()];
const s=[];
for(let r=0;r<10;r++){
s.push(f());
t.push(Date.now());
}
for(let i=0;i<t.length-1;i++)
t[i]=t[i+1]-t[i];
t.pop();
t.sort((a,b)=>a-b);
t.pop();
t.pop();
return t.reduce((a,b)=>a+b);
}
const times=1000;
const bignumber=100000;
const bigarray=new Array(bignumber);
for(let i=0;i<bignumber;i++)
bigarray[i]={x:Math.random(),y:Math.random(),z:Math.random()};
for(let i=0;i<4;i++)
bigarray.push(bigarray[i]);
console.log("idx",m(function(){
let sum=0;
for(let r=0;r<times;r++)
for(let i=0;i<bignumber;i++)
sum+=bigarray[i].x+bigarray[i].y+bigarray[i].z;
return sum;
}));
console.log("idx+4",m(function(){
let sum=0;
for(let r=0;r<times;r++)
for(let i=0;i<bignumber;i++)
sum+=bigarray[i+4].x+bigarray[i+4].y+bigarray[i+4].z;
return sum;
}));
console.log("item",m(function(){
let sum=0;
for(let r=0;r<times;r++)
for(let i=0;i<bignumber;i++){
let item=bigarray[i];
sum+=item.x+item.y+item.z;
}
return sum;
}));
console.log("item+4",m(function(){
let sum=0;
for(let r=0;r<times;r++)
for(let i=0;i<bignumber;i++){
let item=bigarray[i+4];
sum+=item.x+item.y+item.z;
}
return sum;
}));
... and produces output like
idx 2398
idx+4 2788
item 2252
item+4 2303
for me on Chrome. The numbers are runtime in milliseconds of 8 runs (8 best out of 10).
Where
idx is bigarray[b].x+bigarray[b].y+bigarray[b].z, repeated access to the same element with a named index (i)
idx+4 is bigarray[i+4].x+bigarray[i+4].y+bigarray[i+4].z, repeated access to the same element with a calculated index (i+4)
item is item.x+item.y+item.z, so an array element was stored in a variable
item+4 is item.x+item.y+item.z too, just the array element was picked from i+4
Your question is very visibly the outlier here. Repeated access to an element with a "fixed" index (idx case) is already a bit slower than getting out the element into a variable (item and item+4 cases, where +4 is the slower one of course, that addition is executed 800 million times after all). But the 3 times repeated access to an element with a calculated index (idx+4 case) is 15-20+% slower than any of the others.
Here the array is so small that it fits into the L3 cache. If you "move" a couple 0-s from times to bignumber, the overall difference decreases to 10-15%, and anything else than idx+4 performs practically the same.

Correct terminology in nested for loop

I'm trying to store all prime numbers of a given number in an array. I'm running an inner loop from 0 to the count of the outer loop every time the outer loop increases by one. I have if statements inside the inner loop to determine if the number is prime.
function sumPrimes(num) {
var primes = [];
for (var i=0; i<num; i++){
for (var j=0; j<num[i]; j++){
if(num[i][j] !== 1){
if(num[i] % num[i][j] !== 0){
primes.push(num[i]);
}
}
}
}
return primes;
}
sumPrimes(10);
Currently this isn't returning anything. What is the correct way of targeting the counters? essentially I want to say - if (the [number of the outer loop count] is NOT divisible by any number bar 1 and itself from the inner loop that leaves no remainder) {
push this step on the outer loop into my 'primes' array
}
Edit: im not seeking better solutions on how to fill up a prime number array of which im sure there is many ill learn after I solve this. I am right now using this problem as an example for nesting for loops and how to correctly call the different parameters.

Categories