Javascript for loop and splice function - javascript

Good day, I have been trying to solve JS problem where I have an array a = [1, 1, 1, 2, 1, 3, 4]. I have to loop through an array and remove every three elements ie (1,1,1) do some logic, then the next three (1,1,2), and so on.
I have used for loop and splice
a = [1, 1, 1, 2, 1, 3, 4]
tuple = ()
for(j=0; j < a.length; j++){
tuple = a.splice(j, 3)
}
However, loop would not go beyond first round of (1,1,1).
What would be the correct way to loop through this array and remove sets of every three elements.
thank you.

Splice return removed elements from base array in given range. You probable want to use slice which only copy them and doesn't change primary array. You can also check this solution.
let a = [1, 1, 1, 2, 1, 3, 4];
for (j = 0; j < a.length; j++) {
if ( j < a.length - 2 ) {
const touple = [ a[j], a[j+1], a[j+2] ]
console.log( touple )
}
}

splice changes the original array and that must be causing your issue.
So, you can use slice which returns a shallow copy of a portion of the original array (without modifying the original array):
const a = [1, 1, 1, 2, 1, 3, 4]
const doLogic = (arr) => {
console.log(JSON.stringify(arr)) // JSON.stringify is only for one-liner print. You can ignore it.
}
for (let i = 0; i < a.length - 2; i++) {
const picked = a.slice(i, i + 3)
doLogic(picked)
}
Or this, if you want to pick the full array when length is less than 3:
if (a.length < 3) {
doLogic(a)
} else {
for (let i = 0; i < a.length - 2; i++) {
const picked = a.slice(i, i + 3)
doLogic(picked)
}
}

Related

How to multiply array values without nested loops

Problem:
Given an array of integers, return a new array such that each element at index i of the new array is the product of all the numbers in the original array except the one at i.
For example:
if our input was [1, 2, 3, 4, 5], the expected output would be [120, 60, 40, 30, 24].
If our input was [3, 2, 1], the expected output would be [2, 3, 6].
Solution 1 (With Nested loops): I'm able to solve this by nested loops like below:
const input = [1, 2, 3, 4, 5];
function output(items) {
const finalArray = [];
for (let i = 0; i < items.length; i++) {
let multipliedNum = 1;
items.forEach((item, indx) => {
if (i !== indx) {
multipliedNum = multipliedNum * item;
}
});
finalArray.push(multipliedNum)
}
return finalArray;
}
console.log(output(input))
I'm trying to find out another solution without nested loops inside output function? Any help or suggestion really appreciated.
If there are no zero values, you can loop through all the values once to get the product. Then just return the array where each the product is divided by each entry.
However, if there are zeros then there is a little more to be done to check how many there are. One zero is fine but more than 1 means that the value is zero for each entry.
const input = [1, 2, 3, 4, 5];
const input2 = [1, 2, 3, 4, 0];
const input3 = [1, 2, 3, 0, 0];
function output(items) {
let zeroCount = 0;
let totalProduct = 1;
for (let i = 0; i < items.length; i++) {
if (items[i] === 0) {
if (++zeroCount > 1) break;
continue;
}
totalProduct *= items[i];
}
if (zeroCount > 1) {
// more than 1 zero -> all values are 0
return new Array(items.length).fill(0);
} else if (zeroCount === 1) {
// only 1 zero -> only the value that is zero will be the totalProduct
return items.map(item => item === 0 ? totalProduct : 0);
}
// no zero in array -> divide the totalProduct by each item
return items.map(item => totalProduct / item);
}
console.log(output(input))
console.log(output(input2))
console.log(output(input3))
Based on what #Mike said in the comment here's the answer.
const input = [1, 2, 3, 4, 5];
const mulValues = input.reduce((acc, next) => acc * next);
const output = input.map(i => mulValues/i)
console.log(output)
you can do something like that (assuming array doesn't contain zero):
calculate product of all array elements
divide product by element at position [i] to get the desired output
const input = [1, 2, 3, 4, 5];
function output(items) {
const finalArray = [];
const multipliedNum=1;
for (let i = 0; i < items.length; i++) {
multipliedNum *= item[i];
}
for (let i = 0; i < items.length; i++) {
finalArray.push(multipliedNum/item[i]);
}
return finalArray;
}
console.log(output(input))
I know this has already been answered, but I think I have a better one.
If you take this issue by a different approach you will see that the product leaving the value at the index out, is also the product devided by value at the index.
If you know use the reduce function, you can simply calculate the product in one line using:
items.reduce((a, b) => a * b)
and then just divide by the value you want to ignore... like this:
items.reduce((a, b) => a * b) / items[index]
if you now want to compress this in one line instead of wrapping it into a for loop block you can simply copy the array and use the map function and the result could look like this:
result = [...items].map((v, i) => items.reduce((a, b) => a * b) / v)
I hope that this helps you to reduce your code

Why does my sort implementation not work?

I'm trying to sort an integer array without using sort function. I know there are other solutions available on Stack Overflow. I want to know what is wrong with my code. It performs ascending sort except for the first number in the array.
let arr = [2,4,5,1,3,7];
let iterable = true;
let iterationCount = 0;
while(iterable) {
for(var i=iterationCount;i<=arr.length;i++) {
if (arr[i] > arr[i+1]) {
let temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
iterationCount++;
if (iterationCount == arr.length) {
iterable = false;
}
}
console.log(arr)
The output is [2, 1, 3, 4, 5, 7] while it should be [1, 2, 3, 4, 5, 7].
You could change the outer loop for keeping the last index for checking and iterate until before the last index, because in the first inner loop, the max value is now at the greatest index and any further iteration do not need to check the latest last item.
let array = [2, 4, 5, 1, 3, 7],
iterationCount = array.length;
while (iterationCount--) {
for (let i = 0; i < iterationCount; i++) {
if (array[i] > array[i + 1]) {
let temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
}
}
}
console.log(array);
Each time it goes around the inner loop, it moves each element zero or one spaces to the left.
Each time it goes around the outer loop, it ignores one more element from the left hand side.
This means that it assumes that after one loop the left most element is the smallest element (and after two loops, the two left most elements are the two smallest).
However, because of (1) the 1 will have moved from position 3 to position 2 but it needs to be in position 0.
Wikipedia has a selection of popular sorting algorithms you should read up on if you are implementing sorting from scratch.
I fixed the for loop to always start at 0, then it works.
let arr = [2, 4, 5, 1, 3, 7];
for (let j = 0; j < arr.length; j++) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] > arr[i + 1]) {
const temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
console.log(arr)
EDIT: I made the whole thing a little shorter

Efficient algorithm for removing items from an array in place

I'm looking for an efficient JavaScript utility method that in O(n) will remove a set of items from an array in place. You can assume equality with the === operator will work correctly.
Here is an example signature (written in TypeScript for type clarity)
function deleteItemsFromArray<T>(array: T[], itemsToDelete: T[]) { ... }
My thought is to do this in two passes. The first pass gets the indexes that need to be removed. The second pass then compacts the array by copying backwards from the current index being removed through the next index being removed.
Does anyone have code like this already handy or a more efficient way to do this?
P.S. Please don't point out the filter function as that creates a copy of the array, it does not work in place.
Iterate over the array, copying elements that aren't in itemsToDelete to the next destination index in the array. When you delete an element, you don't increment this index.
Finally, reset the length of the array at the end to get rid of the remaining elements at the end.
function deleteItemsFromArray(array, itemsToDelete) {
let destIndex = 0;
array.forEach(e => {
if (!itemsToDelete.includes(e)) {
array[destIndex++] = e;
}
});
array.length = destIndex;
}
const array = [1, 2, 3, 4, 5, 6, 7];
deleteItemsFromArray(array, [3, 5]);
console.log(array);
function deleteItems(array,items){
let itemsToDelete=0;
let indexToSwap = array.length-1;
for(let i = array.length-1,currItem ; i>=0 ; i--){
if(items.includes(array[i]){
[array[i] , array[indexToSwap]] = [array[indexToSwap] , array[i]];
--indexToSwap;
++itemsToDelete;
}
}
array.splice(array.length-itemsToDelete,itemsToDelete);
}
This should work, I haven't tested it.
The idea is to swap the elements to delete to the end of the array. You can remove them at the end how I do in my code or you could use too the pop() function every time.
It's very, very simple - transform itemsToDelete to a Set. This ensures O(1) lookups. Then walk through the array backwards and remove items with Array#splice. In total, that gives you a linear O(n+m) time complexity:
function deleteItemsFromArray<T>(array: T[], itemsToDelete: T[]) {
const setToDelete = new Set(itemsToDelete);
for (let i = array.length - 1; i >= 0; i--) {
const item = array[i];
const shouldBeDeleted = setToDelete.has(item);
if (shouldBeDeleted) {
array.splice(i, 1);
}
}
}
You can save the conversion step if you just make the function accept a set to begin with and change the signature to:
function deleteItemsFromArray<T>(array: T[], itemsToDelete: Set<T>)
function deleteItemsFromArray(array, itemsToDelete) {
for (let i = array.length - 1; i >= 0; i--) {
const item = array[i];
const shouldBeDeleted = itemsToDelete.has(item);
if (shouldBeDeleted) {
array.splice(i, 1);
}
}
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
deleteItemsFromArray(
arr, new Set([1, 3, 5, 7, 8, 9])
)
console.log(arr);
Using copyWithin method will be helpful here.
method 1
Traverse from last index to first index, when ever we find the item to remove just copy/move elements to left.
Though we will be doing times the copy/move, But still have some unnecessary moving elements.
method 2
Traverse from first index to last index, when ever we find the item to remove, identify the group of elements to copy/move. This will avoid the unnecessary moving elements as in method 1.
function deleteItemsFromArray(array, delete_list) {
for (let i = array.length - 1; i > -1; i--) {
if (delete_list.includes(array[i])) {
array.copyWithin(i, i + 1).pop();
}
}
}
// Alternate way, with minimal copy/move group of elements.
function deleteItemsFromArrayAlternate(array, delete_list) {
let index = -1;
let count = 0;
for (let i = 0; i <= array.length; i++) {
if (delete_list.includes(array[i]) || !(i in array)) {
if (index > -1) {
array.copyWithin(index - count + 1, index + 1, i);
}
count += 1;
index = i;
}
}
array.length = array.length - delete_list.length;
}
const array = [9, 12, 3, 4, 5, 6, 7];
deleteItemsFromArray(array, [9, 6, 7]);
console.log(array);

Remove duplicates from sorted array and return length - Must mutate the original array

DISCLAIMER
I am well aware of the duplicate questions, however this one is asking to remove duplicates without making a new array and wants us to mutate the original array.
INSTRUCTIONS
Given a sorted array nums, remove the duplicates in-place such that each element appear only once and return the new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
EXAMPLE
Given nums = [1,1,2],
Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively.
It doesn't matter what you leave beyond the returned length.
ATTEMPT
const removeDuplicates = function(nums) {
for(let i of nums){
if(nums[i] === nums[i]){
nums.splice(i, 1)
}
}
return nums.length;
};
console.log(removeDuplicates([1, 1, 2]));
console.log(removeDuplicates([1, 2]));
// [1, 1, 2] => [1, 2] (Correct)
// [1, 2] => [1] (Incorrect - should be [1, 2])
Am I mutating the array correctly with splice and what do I need to do to correct the 2nd argument?
Also, in leetcode, when I run the first argument, it says it's correct and returns the array of the leftover elements, but the instructions were asking for the length of the new array. Not sure if I'm missing something but why is it not returning the length?
https://imgur.com/5cuhFYf
Here you are:
const removeDuplicates = function(nums) {
for(let i = 0; i < nums.length;){
if(nums[i] === nums[++i]){
nums.splice(i, 1)
}
}
return nums.length;
};
console.log(removeDuplicates([1, 1, 2]));
console.log(removeDuplicates([1, 2]));
let nums = [1,1,2];
nums = [...new Set(nums)].length;
console.log(nums);
nums = [1,1,2];
nums = nums.filter(function(item, pos, self) {
return self.indexOf(item) == pos;
})
console.log(nums)
For each element of the array you need to iterate through all remaining elements of that array, to check for all duplicates. Not sure if this is more performant then making a copy.
const removeDuplicates = function (nums) {
let i = 0;
while (i < nums.length) {
let j = i + 1;
while (j < nums.length) {
if (nums[i] === nums[j]) {
nums.splice(j, 1);
}
else {
j++;
}
}
i++;
}
return nums.length;
};
console.log(removeDuplicates([1, 1, 2]));
console.log(removeDuplicates([1, 2]));
console.log(removeDuplicates([1, 2, 1, 3, 4, 3, 2, 1]));
// [1, 1, 2] => [1, 2] (Correct)
// [1, 2] => [1] (Incorrect - should be [1, 2])
// [1, 2, 1, 3, 4, 3, 2, 1] => [1, 2, 3, 4]
The hint is in the line: It doesn't matter what you leave beyond the returned length.
Whoever is asking you this wants you to move through the array keeping track of 2 pointers: 1) The end of the output array and 2) the current index in the input array.
If you do this, and copy the input to the output pointer only when they're different, you will end up with the correct output, the correct length (from the output pointer) and a little bit of garbage at the end of the array.
const unique = (arr) => {
let output = 0;
for (let input = 0; input < arr.length; input++) {
if (arr[output] !== arr[input]) {
output++;
arr[output] = arr[input];
}
}
return output + 1;
}
const arr = [1, 1, 2, 3, 3, 3, 4, 5, 5, 6, 8, 8, 8, 9, 11];
const length = unique(arr);
console.log(arr, length);
I believe this solution will pass more test cases (at least in my personal testing)
const removeDups = (nums) => {
// since mutating arrays I like to start at the end of the array so when the index is removed it doesn't impact the loop
let i = nums.length - 1;
while(i > 0){
// --i decrements then evaluates (i.e 5 === 4), i-- decriments after the evaluation (i.e 5 === 5 then decrements the last 5 to 4)
if(nums[i] === nums[--i]){
// remove the current index (i=current index, 1=number of indexes to remove including itself)
nums.splice(i,1);
}
}
console.log(nums);
return nums.length;
};
// Test Cases
console.log(removeDups([1,1,2])); // 2
console.log(removeDups([0,0,1,1,1,2,2,3,3,4])); // 5
console.log(removeDups([0,0,0,2,3,3,4,4,5,5])); // 5
Tried the solution provided by Kosh above, but it failed for bigger array [0,0,1, 1, 1, 2, 2, 3, 3, 4]. So ended up writing my own. Seems to work for all tests.
var removeDuplicates = function(nums) {
var i;
for (i = 0; i <= nums.length; i++) {
const tempNum = nums[i];
var j;
var tempIndex = [];
for (j = i+1; j <= nums.length; j++) {
if (tempNum === nums[j]) {
tempIndex.push(j)
}
}
nums.splice(tempIndex[0], tempIndex.length)
}
return (nums.length);
};

How to write my reverse function properly?

i have a problem and i need help for this question.
My reverse function doesn't work the way I want it to.
function reverseArrayInPlace(array){
let old = array;
for (let i = 0; i < old.length; i++){
array[i] = old[old.length - 1 - i];
};
};
let arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
I expect on [5, 4, 3, 2, 1] but i have [5, 4, 3, 4, 5].
Why does it work this way? Please help me understand.
P.S I know about the reverse method.
Variable, with assigned object (array, which is a modified object in fact) - stores just a link to that object, but not the actual object. So, let old = array; here you just created a new link to the same array. Any changes with both variables will cause the change of array.
(demo)
let arr = [0,0,0,0,0];
let bubu = arr;
bubu[0] = 999
console.log( arr );
The simplest way to create an array clone:
function reverseArrayInPlace(array){
let old = array.slice(0); // <<<
for (let i = 0; i < old.length; i++){
array[i] = old[old.length - 1 - i];
};
return array;
};
console.log( reverseArrayInPlace( [1, 2, 3, 4, 5] ) );
P.s. just for fun:
function reverseArrayInPlace(array){
let len = array.length;
let half = (len / 2) ^ 0; // XOR with 0 <==> Math.trunc()
for( let i = 0; i < half; i++ ){
[ array[i], array[len - i-1] ] = [ array[len - i-1], array[i] ]
}
return array;
};
console.log( reverseArrayInPlace( [1, 2, 3, 4, 5] ) );
If you're writing a true in place algorithm, it's wasteful from both a speed and memory standpoint to make an unnecessary copy (as other answers point out--array and old are aliases in the original code).
A better approach is to iterate over half of the array, swapping each element with its length - 1 - i compliment. This has 1/4 of the iterations of the slice approach, is more intuitive and uses constant time memory.
const reverseArrayInPlace = a => {
for (let i = 0; i < a.length / 2; i++) {
[a[i], a[a.length-1-i]] = [a[a.length-1-i], a[i]];
}
};
const a = [1, 2, 3, 4, 5];
reverseArrayInPlace(a);
console.log(a);
Since this is a hot loop, making two array objects on the heap just to toss them out is inefficient, so if you're not transpiling this, you might want to use a traditional swap with a temporary variable.
function reverseArrayInPlace(a) {
for (var i = 0; i < a.length / 2; i++) {
var temp = a[i];
a[i] = a[a.length-i-1];
a[a.length-i-1] = temp;
}
};
var a = [1, 2, 3, 4];
reverseArrayInPlace(a);
console.log(a);
You have in old variable the same array (not by value, but by reference), so if you change any of them you'll have changes in both.
So you should create new array, make whatever you want with them and return it (or just copy values back to your origin array if you don't want to return anything from your function).
It happend like that because:
1) arr[0] = arr[4] (5,2,3,4,5)
2) arr[1] = arr[3] (5,4,3,4,5)
....
n) arr[n] = arr[0] (5,4,3,4,5)
So you can just make a copy (let old = array.slice()) and it'll be woking as you'd expect.

Categories

Resources