function reverseArrayInPlace(array) {
for (let i = 0; i < Math.floor(array.length / 2); i++) {
let old = array[i];
array[i] = array[array.length - 1 - i];
array[array.length - 1 - i] = old;
}
return array;
}
let arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
// → [5, 4, 3, 2, 1]
I am working on the Data Structures of the book Eloquent JavaScript. I mostly understand how this loop works which is cutting the array in half and then start swapping elements at the opposite sides.
but here my problem: the first round of the loop my understanding of "array[i]" is at index 0 and "Math.floor(array.length / 2)" is at index 1. So, we set old = index 0 and then override it to "array[array.length - 1 - i]". the question is first what exactly does -i part mean and what index does this "array[array.length - 1 - i]" located at at the first round of the loop. please someone explain it to I am very visual and I can't pass it if I don’t see each statement in action.
array.length - 1 - i is at the first round array.length - 1 - 0 which is the last element in the array.
So the loop swaps the first and the last element, then increments i. Next round the 2nd and the value before the last one is going to be swapped and so on.
Array: 1234567890
1st Iteration i=0: ^ ^
2nd Iteration i=1: ^ ^
3rd Iteration i=2: ^ ^
4th Iteration i=3: ^ ^
5th Iteration i=4: ^^
easier to understand, and without the problem with odd arr.length
function reverseArrayInPlace(arr)
{
for (let i= 0, j= arr.length -1; i < j; i++, j--)
{
// swapp values of arr[i] and arr[j]
[ arr[i], arr[j] ] = [ arr[j], arr[i] ];
}
return arr;
}
let arrayValues = [1, 2, 3, 4, 5];
reverseArrayInPlace( arrayValues );
document.write( JSON.stringify( arrayValues ));
An array is zero-indexed. Meaning, the first item in an array is at the [0] position. When you use array.length, it returns a total of array elements which can be confusing to you because an array of length 5 will have it's last element located at array[4] (because zero is the first).
For each iteration in your function, it uses the counter i to find the position at the end of the array that matches the iteration.
array.length - 1 - i
The -1 part is explained above. When using array.length, you have to subtract 1 to get the actual position.
I find the following approach helpful for working out what a loop is doing. You start with the array 1,2,3,4,5. Each iteration of the loop has two steps which change the array. The result of each step acting on the array are shown under 1st Step and 2nd Step below. Also shown is the value of i for the loop and the value of old for the loop.
i old 1st Step 2nd Step
0 1 5,2,3,4,5 5,2,3,4,1
1 2 5,4,3,4,1 5,4,3,2,1
1st Step: array[i] = array[array.length - 1 - i];
2nd Step: array[array.length - 1 - i] = old;
Using two indexes into the array, one increasing from 0, the other decreasing from array.length-1, and with a destructuring swap, reverse can be stated a little more concisely like this...
function reverseArrayInPlace(array) {
for (let i=0, j=array.length-1; i<j; i++, j--)
[array[i], array[j]] = [array[j], array[i]];
return array;
}
let arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
// → [5, 4, 3, 2, 1]
Related
I am learning how to code Javascript. Been learning for 6 months now. I'm working through code wars problems to help me with my logic when it comes to algorithms.
I was working on this CodeWars problem Find All Pairs
Here was my logic and code:
SET a pair count variable
SET a sorretd array varible
FOR LOOP iterate though the array
SET index = 0
WHILE index < array argument
Increment index
IF the current iteration is equal to the index + 1
ADD to the pair count varible
RETURN count variable
CODE:
function duplicates(array) {
let pairResult = 0;
let sorrtedArray = array.sort();
for (let index = 0; index < sorrtedArray.length; index++) {
if (sorrtedArray[index + 1] === sorrtedArray[index]) {
pairResult += 1;
index++
console.log(index);
}
}
return pairResult;
}
The test output I was getting with the two test cases were:
console.log(duplicates([1, 2, 5, 6, 5, 2])) ====> 2
console.log(duplicates([1, 2, 2, 20, 6, 20, 2, 6, 2])); ====> 5
I know it was counting 2s three times, at least that is what it seems. Anyways, I had to look at the solution. Below is a code that was almost identical to mine that worked.
function duplicates(array){
//Make the magic happen
const newArray = array.sort((a,b) => a-b);
if (newArray.length <= 1) return 0;
let count = 0;
for (let i = 0; i < newArray.length ; i++) {
if (newArray[i] == newArray[i+1]) {
count++;
i++;
}
}
return count;
}
My question is why are we incrementing the i++ again within the for loop when were incrementing it already when we declare the for loop?
It's there to avoid over-counting duplicates. As the requirements say:
If there are more pairs of a certain number, count each pair only once. E.g.: for [0, 0, 0, 0] the return value is 2 (= 2 pairs of 0s)
For example, given
[2, 2, 2]
you'd want to count one set of pairs.
When a pair is found
for (let i = 0; i < newArray.length ; i++) {
if (newArray[i] == newArray[i+1]) {
count++;
i++;
}
}
you've now checked off both indicies i and i + 1 - if you proceeded to compare indicies i + 1 and i + 2 on the next iteration, you'd be counting the item at i + 1 one too many.
By doing i++ inside the loop, you ensure that you skip over to the next unchecked item, without iterating over something you've already checked, to avoid double-counting.
Question
You are going to be given an array of integers. Your job is to take that array and find an index N where the sum of the integers to the left of N is equal to the sum of the integers to the right of N. If there is no index that would make this happen, return -1.
For example:
Let's say you are given the array {1,2,3,4,3,2,1}: Your function will return the index 3, because at the 3rd position of the array, the sum of left side of the index ({1,2,3}) and the sum of the right side of the index ({3,2,1}) both equal 6.
Answer
function findEvenIndex(arr){
for(let i = 0; i <arr.length; i++){
let arr1 = arr.slice(0, (arr[i] - 1));
let arr2 = arr.slice((arr[i] + 1),);
let arr11 = arr1.reduce((total, item)=>{
return total + item;
}, 0);
let arr22 = arr2.reduce((total, item)=>{
return total + item;
}, 0);
if(arr11 === arr22){
return arr[i];
}
}
return -1;
}
console.log(findEvenIndex([1, 2, 3, 4, 3, 2, 1]))
console.log(findEvenIndex([1, 100, 50, -51, 1, 1]))
console.log(findEvenIndex([1, 2, 3,4,5,6]))
I can't see an error here, but it yields incorrect results. Any ideas?
You have this part:
let arr1 = arr.slice(0, (arr[i] - 1));
let arr2 = arr.slice((arr[i] + 1),);
This is incorrect: arr[i]. That is a value, eg in [2,4,6,8,10] arr[3]==8. You want to slice on the index itself:
let arr1 = arr.slice(0, i - 1);
let arr2 = arr.slice(i + 1,);
Please note: There is another error in the two lines :) I leave that to you. Hint: you're now slicing two values out of the array instead of one. Perform the following code in your head first, then somewhere where you verify your results.
let arr = [0,1,2,3,4]
let x = 2;
console.log(arr.slice(0, x - 1));
console.log(arr.slice(x + 1,));
You could also use the array method findIndex, which, we shouldn't be surprised to learn, finds an index in an array subject to a certain condition.
const sum = (ns) =>
ns .reduce ((total, n) => total + n, 0)
const findBalancedIndex = (ns) =>
ns .findIndex ((_, i) => sum (ns.slice (0, i)) === sum (ns.slice (i + 1)))
console .log (findBalancedIndex ([1, 2, 3, 4, 3, 2, 1]))
console .log (findBalancedIndex ([1, 100, 50, -51, 1, 1]))
console .log (findBalancedIndex ([1, 2, 3, 4, 5, 6]))
Here we include a simple helper function to find the sum of an array, and then we pass a function to findIndex which uses it twice on the elements before the index and those after it. We use the second parameter of the callback function, the index to do this. This means we are skipping the first parameter altogether, and rather than naming it with something like n, we use the somewhat common convention of calling it _, signalling a placeholder we won't use. Note that you don't need to subtract one from the right-hand boundary of slice, since that boundary value is already excluded. And of course, others have pointed out that you need to slice to the index and not the array value at that index.
This finds the first correct index. You would have to use a different technique if you wanted to find all such indices. (That it's possible to have more than one should be clear from arrays like [1, 2, 3, 0, 0, 0, 0, 3, 2, 1] -- the indices for all those 0s would work.)
you return arr[i] when you need to return just i
Giving an array, say [4,2,1,3,5], based on this array, we have a new array, which each number shows the count of elements on its left that are bigger than itself, which is [0,1,2,1,0]. Now write a function with given input of [0,1,2,1,0], return the original array. The range of array is 1 ~ n (n is the size of the array, you can assume all numbers in the original array are consecutive if sorted)
Now to recover the original array, I have tried a way to solve the problem by iterating through the range of array from the end to the front like this:
My approach:
say the range is 1 ~ 5, the original array would be [1, 2, 3, 4, 5] if sorted. Iterate from the end to the beg,
so first 5, there is no element can be bigger than 5, so its maximum count of bigger elements would be 0, then 4 would have 1 as its maximum count of bigger elements, 3 to 2, etc. Store the key-value pairs into an object.
Now iterating through the input from back to front,
0 -> 5
1 -> can be 4, 3, or 2
2 -> can be either 3, 2, or 1
1 -> any number bigger than the first one.
0 -> (can be anything, since 5 is taken, so it can be either 1, 2, 3, or 4)
Simply to map each element of the input as value to its key from the map is not enough. What would be an intuitive way to approach this with optimal performance? (avoiding O(n ^2) if possible.)
Initially make an AVL Tree from numbers 1 to n.
Start from rear i.e. at nth index (considering 1 based index).
Now the high level outline level of the algorithm should look like this:
1. At any ith index, say the number in array(not the originial array) is j
2. Search the number which is at (i-j)th position in your AVL tree(This can be done in O(logn) time. Comment if you need more explanation on this)
3. The element in the AVL tree is your required element. Delete that element from AVL tree.(O(logn))
So the total complexity would be O(nlogn).
Walkthrough
Initially the tree will contain all 5 elements.
You start at index 5(1-based indexing). Element is 0, i.e. i=5, j=0. So 5th largest element which is 5.
Now the tree contains four elements 1,2, 3, and 4. i=4, j=1. So 4-1 i..e 3rd largest element which is 3 in this case.
i=3, j=2. (3-2)rd largest element is 1 since the tree contains (1, 2, 4).
And so on.
Using Tree to find the ith largest number
We can do this by, storing the count of number of nodes in left subtree at the root node. So consider a tree, having elements 1, 2, 3,4 and 5 and tree structure as following:
4(3)
/ \
3(1) 5(0)
/ \
1(0) 2(0)
At root, number 4 is the value and the number in round bracket has the number of nodes in left subtree.
While constructing(insertion and deletion too) the tree, we can maintain the count.
Now, to find the ith node, say we want suppose 3rd nodes in the given tree. We start with the root, it says it has 3 elements smaller than it to the left so we move to left. Now the root i.e. 3 has 1 smaller left element which is less than 3(ith element) so we move to right of it. Subtract 1(the left count)+1(the root itself) out of 3. Now the root is 2 we want 1st element, the left count is 0. Hence the 1st element of the subtree rooted at 2 is 2.
Basic pseudocode is below:
while(true){
count = root->leftCount;
if((count+1)<i){
//move to right
i-=(count+1);
root = root->right;
}
else if(i==(count+1)){
//root is the ith node
break;
} else{
//move to the levft
root=root->left
}
}
You could use Array#reduceRight and use the value as negative index for generating the original array.
var array = [1, 2, 3, 4, 5],
countLeft = [0, 1, 2, 1, 0],
result = countLeft.reduceRight(function (r, a) {
return array.splice(array.length - 1 - a, 1).concat(r);
}, []);
console.log(result);
Shorter version with ES6 and reverse base array.
var array = [5, 4, 3, 2, 1],
countLeft = [0, 1, 2, 1, 0],
indices = array.map((_, i) => i),
result = [];
countLeft.forEach(a => {
result.unshift(array[indices[a]]);
indices = indices.filter((_, i) => i !== a);
});
console.log(result);
At last a proposal with complexity between O(n*(n-1)/2) and O(n).
This version uses a lazy array with progressive reduction of the length for every iteration. At the end, the offset array has zero elements.
var array = [5, 4, 3, 2, 1],
countLeft = [0, 1, 2, 1, 0],
result = [],
length = array.length;
countLeft.forEach((offset => (offset.length = countLeft.length, a => {
var i = offset[a] || 0;
result.unshift(array[i + a]);
offset.length--;
while (i < offset.length) {
offset[i] = (offset[i] || 0) + 1;
i++;
}
}))([]));
console.log(result);
A linear version, heavily inspired by the proposal of Oriol
var array = [1, 2, 3, 4, 5],
countLeft = [0, 1, 2, 1, 0],
swap = [],
i = 0,
l,
temp;
while (i < countLeft.length) {
l = countLeft[i];
while (l) {
swap.push(i + l - countLeft[i]);
l--;
}
i++;
}
i = swap.length;
while (i--) {
temp = array[swap[i]];
array[swap[i]] = array[swap[i] - 1];
array[swap[i] - 1] = temp;
}
console.log(array);
Here is a possible solution. See inline comments for a brief description of this method.
var a = [0,1,2,1,0],
n, b = [], res = [];
// build b = [5,4,3,2,1]
// we use this array to keep track of values to be pushed in res[],
// sorted in descending order
for(n = a.length; n > 0; n--) {
b.push(n);
}
// for each element of a, starting from the end:
// find correct value in b and remove it from b
while(a.length) {
res.push(b.splice(a.pop(), 1)[0]);
}
res = res.reverse();
console.log(res);
Output:
[4, 2, 1, 3, 5]
I propose an approach based on a custom sort, based on mergesort:
Split the array of inversions into two halves
Sort each part recursively, from greatest to lowest, maintaining stability
Merge the two parts
The difference with mergesort is the merge part. If we choose the j-th element of right part instead of the i-th of the left one, it will advance some elements, and therefore its number of inversions must be reduced by that amount.
Like mergesort, the complexity is O(n log n)
function undoInversions(inversions) {
function reorder(arr, from=0, to=arr.length) {
// Based on a stable decreasing mergesort
if(from >= to) return []; // Unusual base case
if(to === from + 1) return [arr[from]]; // Base case
var m = Math.floor((from + to)/2);
var l = reorder(arr, from, m), // Left recursive call
r = reorder(arr, m, to), // Right recursive call
ret = [], i=0, j=0;
while(i < l.length && j < r.length) { // Merge
if(r[j].value - l.length + i >= l[i].value) {
r[j].value -= l.length - i; // Reduce number of inversions
ret.push(r[j++]);
} else {
ret.push(l[i++]);
}
}
while(i < l.length) ret.push(l[i++]); // Merge remaining, if any
while(j < r.length) ret.push(r[j++]); // Merge remaining, if any
return ret;
}
var array = new Array(inversions.length);
reorder(inversions.map(function(inv, idx) {
return {value: inv, originalIndex: idx}; // Keep track of indices
})).forEach(function(obj, idx) {
if(obj.value !== 0) throw 'Invalid input';
array[obj.originalIndex] = idx + 1; // Invert the permutation
});
return array;
}
console.log(JSON.stringify(undoInversions([0,1,2,1,0])));
Here is an example to understand how it works:
[0,1,2,1,0] ~ [4,2,1,3,5]
⤩ ⤧
[0,0,2,1,0] ~ [2,4,1,3,5]
⤩ ⤧
[0,1,0,1,0] ~ [2,1,4,3,5]
⤩ ⤧
[0,0,0,1,0] ~ [1,2,4,3,5]
⤩ ⤧
[0,0,0,0,0] ——→ [1,2,3,4,5]
That is, each array of inversions corresponds to a permutation. We apply a permutation σ which transforms the input to the array of inversions [0,0,0,0,0], which corresponds to the permutation [1,2,3,4,5]. Since we kept track of the original indices, now we only need the to apply σ⁻¹ to [1,2,3,4,5] in order to get the permutation corresponding to the input.
I am using CoffeeScript and I need to go through an Array from the end to the beginning in order to remove elements. It seemed like a trivial task. Here is my original code which works fine if the list length is bigger than 0 but when the list length is 0, the loop runs from -1 to 0 included.
list = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
length = list.length
for i in [length-1..0]
if list[i] >= 3 and list[i] <= 5
list.removeAt(i)
I isolated problem for a length 0 Array:
length = 0
for i in [length-1..0]
console.log '::', i
> :: -1
> :: 0
In regular JavaScript, there would be no problem:
length = 0
for (i = length - 1; i >= 0; i--){
console.log('::', i)
}
// no output
I can't find any way to code a for loop in CoffeeScript that will behave like the JavaScript loop above.
I found an alternative solution using a while loop but it's not pretty. I would like avoiding wrapping my for loop inside an if. Any way to do a CoffeeScript loop for that would behave like the simple JavaScript loop above?
Assuming your end goal is to iterate through a list in reverse order (which seems to be the case based on your original code), you can achieve this in CoffeeScript like
list = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
for i in list by -1
console.log i;
which compiles into
var i, j, list;
list = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
for (j = list.length - 1; j >= 0; j += -1) {
i = list[j];
console.log(i);
}
You can play around with it to check that it also works for empty lists.
If you need the index in the loop body as well, use
for e, i in list by -1
console.log e // array element
console.log i // array index
You said that you need to loop from the end to the beginning so your loop in js would actually be
for (i = length - 1; i >= 0; i--){
console.log('::', i)
}
In CoffeeScript, this could be implemented as follows:
i = length - 1
while i >= 0
console.log '::', i
i--
More CoffeeScript solution:
list = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
new_list = (i for i in list when i < 2 or i > 5)
or only print:
console.log i for i in list when i < 2 or i > 5
When I initialize an array, I found a weird situation.
JS code:
var arr = [1, 2, 3, 4, 5];
for(var i=0, arr2=[]; i < 5; arr2[i] = arr[i++])
console.log(arr2, i);
Output:
[] 0
[1] 1
[1, 2] 2
[1, 2, 3] 3
[1, 2, 3, 4] 4
arr2 initializes to [1, 2, 3, 4, 5] this is what i want
Look at this piece of code :
for(var i=0, arr2=[]; i < 5; arr2[i++] = arr[i])
console.log(arr2, i);
This code initializes arr2 to [2, 3, 4, 5, undefined]
i thought ++ operator operates before next line and both code will be same.
But, it operates differently. Why does this happen?
add explanation
I thought both for loop operates like this
var i = 0;
var arr2 = [];
check i < 5
console.log(arr2, i);
arr2[i] = arr[i];
i = i + 1;
check i < 5
....skip
is this thought wrong?
what is differance between
'arr2[i] = arr[i++];' and
'arr2[i++] = arr[i];'
Edit: removed code snippet because the question code is now fixed
Now, the issue at hand for your question is not the prefix or postfix notation, but the fact that the expression of the for loop (specifically the
arr2[i] = arr[i++] part) will be executed AFTER the loop cycle is completed. This is why your arr2 array is empty in the first iteration, the indexes are all fine but the assignment has just not happened yet.
The complete for syntax as decribed per Mozilla Developer Network is
for ([initialization]; [condition]; [final-expression])
statement
with the note for [final-expression]:
An expression to be evaluated at the end of each loop iteration. This occurs before the next evaluation of condition. Generally used to update or increment the counter variable.
To expand on your edited question regarding postfix position difference:
i++ will increment i after its next usage. So, assuming a starting value of i=3 means
arr[i] = arr2[i++] --> arr[3] = arr2[3]
and after that is done i is incremented to 4.
In the other way around i is incremented after determining the arr index, so it already has the incremented value when accessing arr2:
arr[i++] = arr2[i] --> arr[3] = arr2[4]
If your intent is to copy arr to arr2, there is no need to iterate over each element. Use slice instead:
var arr = [1, 2, 3, 4, 5];
var arr2 = arr.slice();