Remove Duplicates from Sorted Array JS - javascript

I'm trying to understand why my solution to this problem is only partially working.
Problem:
Given a sorted array nums, remove the duplicates in-place such that each element appears only once and returns 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.
My Solution:
var removeDuplicates = function(nums) {
if (nums.length === 0) return 0;
for (let i = 1; i <= nums.length; i++){
if(nums[i] === nums[i-1]){
nums.splice(nums[i], 1);
}
}
return nums.length;
};
This is the outcome I'm getting on leetcode but I don't understand why my solution stops working and the '3s' are not removed?
Output screenshot:

When you splice an array while iterating over it, the whole array will change in-place. For example, let's say that indexes 0 and 1 are duplicates (i is 1). Then, if you remove index 1 from the array, what used to be at index 2 will now be at index 1, and what used to be at index 3 will now be at index 2, etc.
So, you need to subtract 1 from i when an element is removed, otherwise the next element will be skipped.
You also have an off-by-one-error - iterate i from 1 to i < nums.length so you don't go past the end of the array.
You also need to pass the index to remove to splice, not the value to remove.
var removeDuplicates = function(nums) {
for (let i = 1; i < nums.length; i++){
if(nums[i] === nums[i-1]){
nums.splice(i, 1);
i--;
}
}
return nums.length;
};
console.log(removeDuplicates([0, 0, 0]));

Simple version. Using functions already created
let array = new Set(nums);
let values = array.values();
return Array.from(values);

This'd also pass just fine on constant memory:
const removeDuplicates = function (nums) {
let count = 0;
nums.forEach(function (num) {
if (num !== nums[count]) {
nums[++count] = num;
}
});
return nums.length && count + 1;
};

function removeDuplicates(nums) {
let i = 0;
while(i < nums.length - 1) {
i += 1 - ((nums[i] === nums[i+1]) && nums.splice(i, 1).length)
}
return nums.length;
}

C# simple solution:
public int RemoveDuplicates(int[] nums) {
if (nums.Length == 0)
return 0;
var i = 0;
var start = 0;
var end = 0;
while (end < nums.Length)
{
if (nums[start] != nums[end])
{
nums[++i] = nums[end];
start = end;
}
end++;
}
return i + 1;
}

Related

Trying to use.splice to remove duplicates in JavaScript [duplicate]

This question already has answers here:
When looping through values of a JS array, and I remove value, do I need to use while instead of for?
(5 answers)
Closed 1 year ago.
I am trying to make a function that removes the duplicate array elements from an array and returns the same array with no duplicates. I figured this would work:
var removeDuplicates = function(nums) {
nums.sort(function(a,b){ return a-b;});
let len = nums.length;
for (let i = 0; i < nums.length; i++){
if (nums[i] === nums[i+1]){
nums.splice(i,1);
}
}
return nums;
};
console.log(removeDuplicates([0,0,1,1,1,2,2,3,3,4]));
However the console log at the bottom returns nums as [0,1,1,2,3,4] and not the [0,1,2,3,4] I was expecting. Is there any way to modify the for loop or use splice differently (or maybe some other array method that does not return a new array) to remove that last 1 duplicate?
You need to decrement the index because the element above it will be shifted into the current index.
var removeDuplicates = function(nums) {
nums.sort(function(a,b){ return a-b;});
let len = nums.length;
for (let i = 0; i < nums.length; i++){
if (nums[i] === nums[i+1]){
nums.splice(i,1);
--i;
}
}
return nums;
};
console.log(removeDuplicates([0,0,1,1,1,2,2,3,3,4]));
Alternatively, looping backwards avoids this issue altogether.
var removeDuplicates = function(nums) {
nums.sort(function(a,b){ return a-b;});
let len = nums.length;
for (let i = nums.length - 1; i >= 0; i--){
if (nums[i] === nums[i+1]){
nums.splice(i,1);
}
}
return nums;
};
console.log(removeDuplicates([0,0,1,1,1,2,2,3,3,4]));
A simpler method would be to use a Set.
var removeDuplicates = function(nums) {
return [...new Set(nums.sort((a,b)=>a-b))];
};
console.log(removeDuplicates([0,0,1,1,1,2,2,3,3,4]));

How do I utilize the length of an array in a function (in JavaScript) when the array is being passed as a parameter and the length does not exist yet?

I'm trying to write a function that takes an array of strings(strarr) and an integer(k) as parameters and returns the longest string made up of k amount of consecutive strings within the array, not separated by commas. I need to call on strarr.length quite often during the function, but I keep getting an error saying that it cannot read that parameter's length.
This is my first time dealing with this issue and I have not found great internet search results for solutions. I suspect that I'm missing something very obvious. I have tried pushing the values of the strarr array parameter into a new array and still no luck.
So if I had const = ['apple','pineapple','banana','strawberry'] passed as my strarr parameter and 2 passed as k, then it should return 'bananastrawberry' because it is the longest consecutive pair of strings within the array.
const arr = ['apple', 'pineapple', 'banana', 'strawberry']
function longestConsec(strarr, k) {
if (strarr.length === 0) {
return "";
} else if (k > strarr.length) {
return "";
} else if (k <= 0) {
return "";
}
let longest = "";
let strLeng = 0;
for (let i = 0; i < strarr.length; i++) {
for (let j = i + (k - 1); j > 0; j--) {
strLeng += strarr[j].length;
}
if (strLeng > longest.length) {
longest = strarr.slice(i, (i + k)).join("");
}
}
return longest;
}
console.log(longestConsec(arr, 2))
As mentioned, you are trying to access an index that doesn't exist in you array.
A quick fix might be:
const arr = ['apple', 'pineapple', 'banana', 'strawberry']
function longestConsec(strarr, k) {
if (strarr.length === 0) {
return "";
} else if (k > strarr.length) {
return "";
} else if (k <= 0) {
return "";
}
let longest = "";
let strLeng = 0;
for (let i = 0; i < strarr.length; i++) {
for (let j = i + (k - 1); j > 0; j--) {
if (j >= strarr.length) {
break;
}
strLeng += strarr[j].length;
}
if (strLeng > longest.length) {
longest = strarr.slice(i, (i + k)).join("");
}
}
return longest;
}
console.log(longestConsec(arr, 2))
But I would suggest to see if there is better solution than adding a break statement.
Mistakes you did
In the inner for loop, for (let j = i + (k - 1); j > 0; j--), you're counting from i + k - 1. But what if i is the last index of array (strarr.length == 10 and i == 9) and k == 2? Then your loop starts at j = 9 + 2 - 1 = 10 and one line below, you try to do strLeng += strarr[10].length, but strarr[10] is not defined.
It also seems unnecessary to create strings before you're done finding the longest one. You could instead just remember start index of your last longest string instead.
How to make it better
Let's look at the requirements. For each i in the array, you want to merge k consecutive strings and keep the longest combination. From that follows:
i + k - 1 must never be larger than strarr.length. Since the i is the only variable here, we need to limit it by only looping up to strarr.length - k + 1.
If k == strarr.length, there is only one string you can make - strarr.join("")
Finally, there's an idea that you probably do not need nested loop at all. For every i, you simply subtract the length of the last string in your current window and add a new one. See image:
So with that in mind, I would propose following version of your code:
function longestConsec(strarr, k) {
// Cannot create any joined string with k greater than length
if(strarr.length < k) {
return "";
}
else if(k <= 0) {
return "";
}
else if(strarr.length == k) {
return strarr.join("");
}
let longestIndex = -1;
let longestLength = 0;
// length of our current group of strings
let currentLength = 0;
const maxLen = strarr.length;
for(let i=0; i<maxLen; ++i) {
// Forget the first strings length
if(i >= k) {
currentLength -= strarr[i-k].length;
}
// add the current strings length
currentLength += strarr[i].length;
// check if this is the largest length and save it's index
// Only possible after processing at least k strings
// Eg when i==1, we already went through 2 strings at this point
if(i >= k-1) {
if(currentLength > longestLength) {
const startIndex = i-k+1;
longestLength = currentLength;
longestIndex = startIndex;
}
}
}
return strarr.slice(longestIndex, (longestIndex + k)).join("");
}
Here's a jsFiddle test: https://jsfiddle.net/32g5oqd1/2/

How to code all for all cases of Two Sum javascript problem

I have been working on the two sum problem for the past few hours and can't seem to account for the case where there are only two numbers and their sum is the same as the first number doubled.
The result should be [0,1], but i'm getting [0,0].
let nums = [3,3];
let targetNum = 6;
function twoSum(nums, target) {
for (let i = 0; i < nums.length; i++) {
for (let b = i+1; b < nums.length; b++) {
if ((nums[i] + nums[b]) == target) {
return [nums.indexOf(nums[i]), nums.indexOf(nums[b])];
}
}
}
}
console.log(twoSum(nums, targetNum))
Two Sum
My approach uses a javascript object and completes the algorithm in O(n) time complexity.
const twoSum = (nums, target) => {
let hash = {}
for(i=0;i<nums.length;i++) {
if (hash[nums[i]]!==undefined) {
return [hash[nums[i]], i];
}
hash[target-nums[i]] = i;
}
};
console.log(twoSum([2,7,11,15], 9)); // example
This is not the way to solve the problem. Step through the array and save the complement of the target wrt the number in the array. This will also solve your corner case.
You should consider, indexOf(i) -> start from the first element, returns the index when match found! That is why in your code, nums.indexOf(nums[i]) and nums.indexOf(nums[b]) which is basically 3 in all two cases, it will return 0, cause 3 is the first element in array.
instead of doing this, return the index itself.
let nums = [3,3];
let targetNum = 6;
function twoSum(nums, target) {
for (let i = 0; i < nums.length; i++) {
for (let b = i+1; b < nums.length; b++) {
if ((nums[i] + nums[b]) == target) {
return i + "" +b;
}
}
}
}
console.log(twoSum(nums, targetNum))

array function that generates array with subset range of numbers

I am trying to create a function that builds an array up to a number set by the function parameter, with an if condition on being included based on whether the remainder is zero. The last number in the array should be no higher than the parameter. Here's what I came up with so far --
function justThreesUpTo(num) {
var array = [];
array.length = num;
for (i = 1; i < array.length; i++) {
if(i % 3 === 0){
array.push(i);
}
else i;
}
array.splice(0, num);
return array;
}
When I console log this, with justThreesUpTo(20), I get --
// [ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42 ]
I see the issue being setting the limiter at array.length, which maxes out the number of items that can be in the array, but I can't figure out what else to call to make sure the last number in the array goes no higher than the "num" parameter specified by the function call. Any ideas?
Setting an array's length to something before the array is populated isn't a great idea - better to just iterate over the num itself. For example
for (var i = 1; i < num; i++) {
// push to array if i % 3 === 0
Your else i won't do anything - you can just leave it off completely.
You could make your code a whole lot shorter and cleaner if you wanted:
function justThreesUpTo(num) {
const length = Math.floor(num / 3);
return Array.from({ length }, (_, i) => (i + 1) * 3);
}
console.log(justThreesUpTo(20));
Modifying an array while looping over it (or its indices, which is what you’re doing with i < array.length) is a recipe for confusion. Start with an empty array and compare with num instead:
function justThreesUpTo(num) {
var array = [];
for (var i = 1; i < num; i++) {
if (i % 3 === 0) {
array.push(i);
}
}
return array;
}
Now you can optimize the check out of that entirely by moving up the appropriate amount each time.
function justThreesUpTo(num) {
var array = [];
for (var i = 3; i < num; i += 3) {
array.push(i);
}
return array;
}
(In your original code, the entire first num holes created by array.length = num; are unused and get spliced off, and else i does nothing.)
You can try with a simple while loop
function justThreesUpTo(num) {
var array = [];
var i = 0;
while (i < num) {
if(i % 3 === 0){
array.push(i);
}
i++;
}
return array;
}
console.log(justThreesUpTo(20));
You can use map method and spread syntax in order to write a clean solution.
function justThreesUpTo(num) {
return [ ...Array(Math.floor(num/3)).keys() ].map((_,i)=> (i+1) * 3);
}
console.log(justThreesUpTo(20));
Hmm. Looks like it was a pretty simple solution. Changed the limiter from "array.length" to "num", and it worked fine.
function justThreesUpTo(num) {
var array = [];
array.length = num;
for (i = 1; i < num; i++) {
if(i % 3 === 0){
array.push(i);
}
else i;
}
array.splice(0, num);
return array;
}
Never mind!
Use while with i+=3; inside the while loop:
function justThreesUpTo(num) {
var array = [];
var i = 0;
while(i<num){
array.push(i);
i+=3;
}
return array;
}
console.log(justThreesUpTo(20));

Having trouble getting rid of duplicates in JavaScript.

var numberArray = [1,2,3,4, 5,6,7,8,9, 9, 4];
var newArray = [];
function primeChecker(arrayCheck){
for (var i = 0; i < arrayCheck.length; i++){
if (Math.sqrt(arrayCheck[i]) % 1 === 0) {
newArray.push(arrayCheck[i]);
}
}
for (var x = 0; x < newArray.length; x++){
newArray.sort();
if (newArray[x] === newArray[x -1]){
newArray.splice(newArray[x-1]);
}
}
}
primeChecker(numberArray);
console.log(newArray);
The returned array is [ 1, 4, 4, 9 ]. The function successfully gets rid of the repeating 9s but I am still left with two 4s. Any thoughts as to why this might be? I am a JavaScript beginner and not totally familiar with the language.
Loop backwards. When you remove the item from the array the array gets shorter.
https://jsfiddle.net/2w0k5tz8/
function remove_duplicates(array_){
var ret_array = new Array();
for (var a = array_.length - 1; a >= 0; a--) {
for (var b = array_.length - 1; b >= 0; b--) {
if(array_[a] == array_[b] && a != b){
delete array_[b];
}
};
if(array_[a] != undefined)
ret_array.push(array_[a]);
};
return ret_array;
}
console.log(remove_duplicates(Array(1,1,1,2,2,2,3,3,3)));
Loop through, remove duplicates, and create a clone array place holder because the array index will not be updated.
Loop backward for better performance ( your loop wont need to keep checking the length of your array)
You do not need insert the number that already is in newArray, you can know what element is in the array with the method indexOf.
Try it in the if, and you can delete the second cicle for.
Something like this:
if (Math.sqrt(arrayCheck[i]) % 1 === 0 && newArray.indexOf(arrayCheck[i])==-1)

Categories

Resources