I've been completing challenges on FreeCodeCamp and stumbled upon this solution for an algorithm. Can't comprehend how the if else statement works here.
function chunkArrayInGroups(arr, size) {
var temp = [];
var result = [];
for (var a = 0; a < arr.length; a++) {
if (a % size !== size - 1)
temp.push(arr[a]);
else {
temp.push(arr[a]);
result.push(temp);
temp = [];
}
}
if (temp.length !== 0)
result.push(temp);
return result;
}
Why is temp = [] at the end of the else block?
temp = [] means "reset the temp variable to an empty array"
in the if block, the arr[a] element is pushed at the end in the temparray.
in the else block, the same happens AND the whole current temp array is added at the end of the big result array of arrays, and the temparray is reset to the empty array.
Cannot say much more since there is not data or context written in your question. Hope this has answered your question.
The function divides an array into chunks of small arrays where the 'size' parameter defines the length of each chunks array.
The algorithm works as follows:
iterate each array element (for loop) until main_array elements index
is less than chunk array size (a % size !== size - 1), and push elements into
temporary array.
else block - push element into temp array, push temp array into results array
and create new empty temp array ();
at the end if temp array length is not 0 then push it also into results array.
that's it :)
Seems like the code tries to divide an array into chunks of smaller arrays of size size. Let's say you have a big bag full of apples and you want to reduce it to smaller apple bags each having at max 2 apples. How would you do in real life? You will go through the apples starting from first one and keep putting them in a small bag until it gets full. Then you move to another small bag and start to fill that it. Same thing is done here.
Inside if there is a condition checking if the temporary chunks have been filled and if not then keep pushing to the current temporary array. If yes then create a new array and keep pushing to it. temp=[] ensures the resetting.
It happens #Peter Pavluchenko. Let's walk through the code and see what it's doing.
the caller is specifying the size of chunks.
a % size !== size -1
% is a mod operator. so when a is divided by size what is the result? is the result not equal to size -1?
if the result of 'a' being divided by size is not equal to size -1, than we push the item to the end of our temp array.
If the result of 'a' being divided by size is equal to size - 1, we have found our first chunk! So lets' add the item to our temp array and add the temp array to our result array.
temp array
- item 1
- item 2
result array
- item
-- item 1
-- item 2
so when you do result[0], you will get another array.
chunk = result[0];
chunk[0] = the actual object you might be interested in.
After we have pushed the temp array to the end of our result array, we need to reset it so we can start collecting items for the next chunk.
The last 'if' is looking for situations like, you are in the middle of a chunk, but it's not a whole chunk as specified by the size argument. so we have some objects in our temp array that we need to add to the result array before we leave the method.
It appears that the task is to split up a given array (arr) into an array of arrays, of which each array has length size - except perhaps for the last which has any "leftovers" if the length of array is not an exact multiple of size.
The way the given solution works is to go through arr, pushing its elements into the temporary array temp. But every size steps - this corresponds to the else condition inside the for loop - the temp array will have the correct length (namely size), so then the entire temp array, as a single element, is appended to the result array, and then returned to being an empty array to start over.
This way, result ends up with the chunks of size size as required. The final if statement just adds the "leftovers" on - that is, anything left in temp. (But we check that it isn't empty, otherwise if the length was an exact multiple you'd have an unwated empty array at the end of result.)
Related
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) );
}
This is one of the leetcode problems. In this problem we are to build an array from permutation and the challenge is to solve the problem in O(1) space complexity, so does the solution fulfills the criteria or not? One more thing if we are manipulating the same array but increasing its length by appending the elements at the end, does it mean that we are assigning new space hence causing O(N) space complexity during the execution of the program?
var buildArray = function(nums) {
let len = nums.length
for (let i = 0; i < len; i++) {
nums.push(nums[nums[i]]);
}
nums = nums.splice(len, nums.length);
return nums;
};
This is O(n) space complexity, you can't "cheat" by pushing your data on the input array, because you are using extra space anyways.
This code would be the equivalent of storing your data in a new array, and then returning that
I would guess the target of this space complexity limitation is for you to reach to a solution using pointers to mutate the input array
The space complexity still is O(n). The input array has n length. When you push in the array it basically allocates one more memory location and then update the array.
By pushing the elements in the array you are still using extra n space.
In C++, to resize the array code would be written as:
int* arr = new int[10]
int* resize_arr = new int[size*2];
for(int i = 0; i < size; i++)
resize_arr[i] = arr[i];
arr = resize_arr;
delete[] resize_arr;
after using all the allocated space, to add more elements you need to create a new array then copy the elements.
All these steps are done in one line in python. It does not mean you are not using more space.
Since for any given nums you are looping at least once the entire array and then do an O(1) operations ( keys lookup & push ), it would be safe to say this is an O(n) solution.
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.
var arr =[1,2,3,4];
var ct = arr.length;
for( var i=0;i<ct;i++){
ct--;
arr[i]+=i;
}
console.log(arr);//1,3,3,4
Explain the code, it's confusing me.
Your loop run 2 times.
First time with i = 0, first item in array increase 0 -> 1
Second time with i= 1, second item in array increase 1 -> 3
The ct decrease so that your loop can not reach third time, array item remain keep old value.
Well, you are not really changing the size of the array.
You create an array with 4 elements (length = 4)
You record the length into an integer, this is a copy of the value
Inside the loop, you decrease the value of ct, which is a copy of the length, so it does not effect the array at all
You increase the current element of the array by what i is at the time, so for the first iteration of the loop, you do 1 + 0
So you see, no changes are made to the length of the array, only the values that the array contains.
In first example I created empty array of length 1000:
var arr = new Array(1000);
for (var i = 0; i < arr.length; i++)
arr[i] = i;
In second example created empty array of length 0:
var arr = [];
for (var i = 0; i < 1000; i++)
arr.push(i);
Testing in Chrome 41.0.2272.118 on OS X 10.10.3 and first block run faster. Why? Because JavaScript-engine knows about array size?
Benchmark is here http://jsperf.com/poerttest/2.
If you don't specify the array size it will have to keep allocating more space. But if you specify the size at the beginning, it only allocates once.
Yes. When you allocate size, interpreter knows that it has allocate only 1000 element memory/space. So, when you insert element, it is just one operation. But when you declare dynamic array, 2nd scenario your case, interpreter has to increase size of the array and then push the element. It is 2 operations!
Another possibility could have been that push() is more expensive than assigning to a fixed position. But tests show it is not the case.
What happens is that empty arrays get a relatively small starting capacity (either hash pool or actual array), and increasing that pool is expensive. You can see that by trying with lower sizes: at 100 elements, the performance difference between Array(100) and [] disappears.