Checking for duplicates within an array - javascript

I am trying to check for matching instances of arrays within a larger array. To do this, I am implementing a condition where if two of three numbers in an array match two of the three members of any of the arrays within a larger array, there is a continue statement to go back to a previous loop:
var threeSum = function (nums) {
let result = [];
for (let i = 0; i < nums.length - 2; i++) {
for (let j = i + 1; j < nums.length - 1; j++) {
loop1: for (let k = j + 1; k < nums.length; k++) {
if (nums[i] + nums[j] + nums[k] === 0) {
let smallArray = [nums[i], nums[j], nums[k]].sort((a, b) => a - b);
for (let l = 0; l < smallArray.length && result[l]; l++) {
if (
smallArray[0] == result[l][0] &&
smallArray[1] == result[l][2]
) {
console.log("we already have this array")
continue loop1;
}
}
result.push(smallArray);
}
}
}
}
return result;
};
Thus, for example threeSum([-1, 0, 1, 2, -1, -4]) should return [[-1, 0, 1], [-1, -1, 2]] when is instead is returning [[-1, 0, 1], [-1, -1, 2], [-1, 0, 1]]. I have checked using the console.log in the inner most conditional, and the if statement is never returning as true, so it is never entering the continue command. But the first and third arrays should meet the requirements of this, so it seems as if when checking the third array, the function should kick back the command.
I am a bit puzzled as to what is going wrong.

A single loop approach but with recursion and filtering for arrays with same values.
function threeSum(array, temp = [], sum = 0, i = 0) {
if (temp.length === 3) return sum ? [] : [temp];
const result = [];
while (i < array.length) {
result.push(...threeSum(array, [...temp, array[i]], sum + array[i], ++i));
}
return result.filter((s => a => (b => !s.has(b) && s.add(b))([...a].sort().join('|')))(new Set));
}
console.log(threeSum([-1, 0, 1, 2, -1, -4]));

Related

Adding Negative Integers in Javascript

I am trying to write a function that returns the first two values in order of appearance that add to the sum. My function works fine with positive numbers, but not with negative. Can anyone tell me what I am doing wrong?
This function return an empty array:
const sumOfPairs = (arr, sum) => {
const answer = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
if (arr[i] + arr[j] === sum && i != j) {
answer.push(arr[j], arr[i]);
}
break;
}
}
return answer;
}
console.log(sumOfPairs([1, -2, 3, 0, -6, 1], -6));
Your implementation doesn't work for all positive numbers either. Try 3 as sum.
I believe you want to put the break statement inside the if statement or just replace the body of the if statement with return [arr[j], arr[i]]:
const sumOfPairs = (arr, sum) => {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
if (arr[i] + arr[j] === sum && i != j) {
// You can return here if you only want to find *one* pair
// You also need add some additional logic if you want them
// to be in the same order as in the original array:
return i < j ? [arr[i], arr[j]] : [arr[j], arr[i]]
}
}
}
return []
}
console.log(sumOfPairs([1, -2, 3, 0, -6, 1], -6));
The current location of the break statement in your loop causes the inner loop to terminate after its first iteration! Therefore the inner loop is equivalent to testing whether the sum of the current outer loop's item (arr[i]) and 1 (the first element in the array) is equal the provided sum.

How to return multiple results into an array?

The task is to build a function where it receives an array and a number that will work as a limit. The thing is that it should return an array with the resulting booleans like this:(i.e. [true, false, false]). But I can't figure out how.
I tried using a for loop to stuff an empty array, but it returns just false.
function aperturas(arrayDeIngresosSemanales, cantMinEst) {
for (var i = 0; i < arrayDeIngresosSemanales.length; i++) {
var a = 0;
var arr = arrayDeIngresosSemanales[i];
for (var j = 0; j < arr.length; j++) {
if (arr[j] <= 0) {
a = a + 1;
}
}
if (a >= cantMinEst) {
return true;
} else {
return false;
}
}
}
aperturas([0, 0, 3, 0], [1, 2, 4, 5], [0, 0, -1], 3);
return breaks out of the function - have a result array instead:
function aperturas(arrayDeIngresosSemanales, cantMinEst) {
let result = [];
// ...
if (a >= cantMinEst) {
result.push(true);
}
result.push(false);
}
// ...
return result;
}
You could even remove the if statement:
result.push(a >= cantMinEst);
You shouldn't return immediately after evaluating an array element.
Create a result array and push result of each array's evaluation and return the result.
Also, you aren't calling the function properly. First argument is an array of arrays, you have to call it like aperturas([[0, 0, 3, 0], [1, 2, 4, 5], [0, 0, -1]], 3)
function aperturas(arrayDeIngresosSemanales, cantMinEst) {
// Create an array to store the result.
var result = [];
for (var i = 0; i < arrayDeIngresosSemanales.length; i++) {
var a = 0;
var arr = arrayDeIngresosSemanales[i];
for (var j = 0; j < arr.length; j++) {
if (arr[j] <= 0) {
a = a + 1;
}
}
// Now compare with limit after iterating completely over the array.
if (a >= cantMinEst) {
result.push(true);
} else {
result.push(false);
}
}
// After iterating over all the arrays, return the result.
return result;
}
console.log(aperturas([[0, 0, 3, 0], [1, 2, 4, 5], [0, 0, -1]], 3));
Or, alternatively if you want to use a more semantic JS feature than the for loop, you could keep the return statements but use the map function instead. That would look something like this:
arrayDeIngresosSemanales.map((arr) => {
var a = 0;
for (var j = 0; j < arr.length; j++) {
if (arr[j] <= 0) {
a = a + 1;
}
}
if (a >= cantMinEst) {
return true;
} else {
return false;
}
})
By that same token, you could also replace the inner for loop with a reduce, but that will be left as an exercise for the reader.

Javascript, Merging two sorted arrays: Can someone please tell me why this isn't giving the correct mergeSort?

I've tried merging the two arrays but the output i get is [ 0, 3, 3, 4, 4 ]
function mergeSortedArrays(arr1, arr2) {
var i = 0;
var j = 0;
var arr3 = [];
if (arr1 === undefined || arr1.length == 0) {
return arr2;
}
if (arr2 === undefined || arr2.length == 0) {
return arr1;
}
while (i < arr1.length - 1 && j < arr2.length - 1) {
if (arr1[i] < arr2[j]) {
arr3.push(arr1[i]);
i++;
} else {
arr3.push(arr2[j]);
j++;
}
}
return arr3;
}
console.log(mergeSortedArrays([0, 3, 4, 31], [3, 4, 6, 30]));
For this example, i know i haven't accounted for the case in which arrays are of different size but that is for a later problem. The code currently isn't even working for the basic case. It does not iterate all the way through and breaks midway. Can someone please address this problem. I've worked around with the while loop but the code still doesnt work.
You are making two mistakes.
In the while loop condition check of length of array not length - 1. This will not add last elements of both array.
Your while loop will end when one of the array will be completely loop because of &&. So after while add the remaining elements of other array.
function mergeSortedArrays(arr1, arr2) {
var i = 0;
var j = 0;
var arr3 = [];
if (arr1 === undefined || arr1.length == 0) {
return arr2;
}
if (arr2 === undefined || arr2.length == 0) {
return arr1;
}
while (i < arr1.length && j < arr2.length) {
if (arr1[i] < arr2[j]) {
arr3.push(arr1[i]);
i++;
} else {
arr3.push(arr2[j]);
j++;
}
}
if(i === arr1.length){
return arr3.concat(arr2.slice(j))
}
else if(j === arr2.length){
return arr3.concat(arr1.slice(i))
}
}
console.log(mergeSortedArrays([0, 3, 4, 31], [3, 4, 6, 30]));
First of all, the loop needs to iterate till arr1.lenght and arr2.lenght not till lenght - 1,
while (i < arr1.length && j < arr2.length )
Also, after the loop breaks, the reason being either one of the array loop variables has a false condition, before returning the array, you need to check if there are values left which are not yet inserted. This is important.
You can do this:
arr3.concat(arr1.slice(i)).concat(arr2.slice(j));
Hope it helps.
There are two things missing in your code:
The while loop is running for i < arr1.length - 1 for arr1 and j < arr2.length - 1 for arr2. That means it will run till 3rd index of arr1 and arr2. So the last index will not be executed in the loop. So you should refactor the loop as i < arr1.length for arr1 and j < arr2.length for arr2.
Next missing thing, you need to add the loop for remaining elements of remaining array. That means when you execute the code then all the elements of one loop will be pushed in the arr3 then one elements of another array will be missed and it will not be pushed in the arr3 (for same length of both array) and/or more than one elements of another array will be missed (for different length of both array). So you need to push the remaining elements of another array to the arr3.
I have added the refactored code of your code.
function mergeSortedArrays(arr1, arr2) {
var i = 0;
var j = 0;
var arr3 = [];
if (arr1 === undefined || arr1.length == 0) {
return arr2;
}
if (arr2 === undefined || arr2.length == 0) {
return arr1;
}
while (i < arr1.length && j < arr2.length) {
if (arr1[i] < arr2[j]) {
arr3.push(arr1[i]);
i++;
} else {
arr3.push(arr2[j]);
j++;
}
}
while (i < arr1.length) {
arr3.push(arr1[i]);
i++;
}
while (j < arr2.length) {
arr3.push(arr2[j]);
j++;
}
return arr3;
}
OR
you can use spread operator to merge both array and then you can use sort method of JS.
function mergeTwo(arr1, arr2) {
let result = [...arr1, ...arr2];
return result.sort((a,b) => a-b);
}
There is a simple one line solutions that does involve a resort. Using default parameters to deal with undefined arrays, and the spread operator to populate a new array with the two merging arrays. The result array just needs to be sorted and then returned.
const mergeSorted = (a1 = [], a2 = []) => [...a1, ...a2].sort((a, b) => a - b);
Example
const mergeSorted= (a1 = [], a2 = []) => [...a1, ...a2].sort((a, b) => a - b);
console.log(mergeSorted([0, 3, 4, 31], [3, 4, 6, 30]).join(", "));
Or without the sort and using parameter defaults to avoid needing to test arrays for undefined
function mergeSorted(a1 = [], a2 = []) {
const res = [];
var idx1 = 0, idx2 = 0;
while (idx1 < a1.length || idx2 < a2.length) {
if (idx1 < a1.length && idx2 < a2.length) {
res.push(a1[idx1] < a2[idx2] ? a1[idx1++] : a2[idx2++]);
} else {
res.push(idx1 < a1.length ? a1[idx1++] : a2[idx2++]);
}
}
return res;
}
Example
function mergeSorted(a1 = [], a2 = []) {
const res = [], l1 = a1.length, l2 = a2.length;
var i1 = 0, i2 = 0;
while (i1 < l1 || i2 < l2) {
if (i1 < l1 && i2 < l2) { res.push(a1[i1] < a2[i2] ? a1[i1++] : a2[i2++]) }
else { res.push(i1 < l1 ? a1[i1++] : a2[i2++]) }
}
return res;
}
console.log(mergeSorted([0, 3, 4, 31, 99], [3, 4, 6, 30]).join(", "));
In the while condition when i or j indexes reach the length of the corresponding array finishes the loop returning an array of the lower length.
This works:
function mergeTwoSortedArrays(arr1, arr2) {
let merged = [];
let index1 = 0;
let index2 = 0;
let current = 0;
while (current < (arr1.length + arr2.length)) {
let isArr1Depleted = index1 >= arr1.length;
let isArr2Depleted = index2 >= arr2.length;
if (!isArr1Depleted && (isArr2Depleted || (arr1[index1] < arr2[index2]))) {
merged[current] = arr1[index1];
index1++;
} else {
merged[current] = arr2[index2];
index2++;
}
current++;
}
return merged;
}
Fore more info : https://wsvincent.com/javascript-merge-two-sorted-arrays/

Weird bug in Javascript splice method

I have an array which contains "Zeros" and I want to move all of
the "Zeros" to the last indexes of the array.
The expected output is:
[1,2,3,0,0,0,0]
But instead I get:
[1,2,0,3,0,0,0]
let a = [0, 1, 2, 0, 0, 3, 0];
let count = 0;
let len = a.length;
for (i = 0; i < len; i++) {
if (a[i] == 0) {
count = count + 1;
a.splice(i, 1);
}
}
for (j = 0; j < count; j++) {
a.push(0);
}
console.log(a);
When you remove the item from the array all the element shift down by one. When you advance your index (i++), you skip the shifted down item in the array which happens to be successive zero in the array.
Solution: Do the for next loop backward and it'll work.
Because splice changes the length of the array, you could iterate from the end of the array and splice the found value directly to the last index.
With this approach, you need only a single loop.
var a = [0, 1, 2, 0, 0, 3, 0],
i = a.length;
while (i--) {
if (a[i] === 0) {
a.splice(a.length, 0, ...a.splice(i, 1));
}
}
console.log(a);
A shorter approach without splicing - and starting from zero.
var a = [0, 1, 2, 0, 0, 3, 0],
i, j = 0;
for (i = 0; i < a.length; i++) {
if (a[i] !== 0) {
[a[j], a[i]] = [a[i], a[j]]; // swap
j++;
}
}
console.log(a);
You can do it much simpler with Array.prototype.sort():
const array = [0, 1, 2, 0, 0, 3, 0];
const sortedArray = array.sort((a, b) => {
if (a === 0) {
return 1;
}
if (b === 0) {
return -1;
}
return a - b;
});
console.log(sortedArray);
In the for loop when you splice the array the array and it length are changed.
for that you must fix the i in the for loop by subtract 1
i++;
and fix the length by subtract 1 or reget the length again
let a = [0, 1, 2, 0, 0, 3, 0];
let count = 0;
let len = a.length;
for (i = 0; i < len; i++) {
if (a[i] == 0) {
count = count + 1;
a.splice(i, 1);
len = a.length;
i--;
}
}
for (j = 0; j < count; j++) {
a.push(0);
}
console.log(a);
Instead of splicing the Array over and over again, here is a different approach:
let a = [0, 1, 2, 0, 0, 3, 0];
// create some more (random) data
for (let i = a.length; i < 30; ++i)
a[i] = Math.floor(Math.random() * Math.random() * 10);
console.log(""+a);
let i = 0, j = 0, len = a.length;
// move non-0 values to the front
while (i < len) {
if (a[i] !== 0) {
a[j++] = a[i];
}
++i;
}
// fill the end of the list with 0
while (j < len) a[j++] = 0;
console.log(""+a);
You could add i--; and len--; each time you use splice:
let a = [0, 1, 2, 0, 0, 3, 0];
let count = 0;
let len = a.length;
for (i = 0; i < len; i++) {
if (a[i] == 0) {
count = count + 1;
a.splice(i, 1);
i--; len--;
}
}
for (j = 0; j < count; j++) {
a.push(0);
}
console.log(a);
This is because when you splice 1 element, the keys of the array are shifted down by one, so the key of the next element you want to check is the same as the one you just removed. The len is also corrected with len--; because we just removed an element.
While this answer is the correct way of doing this using your original plan, it is kind of a fix. Your problem was that you loop over an array, and that array loses elements during the loop, and generally the right approach in these situations is to loop backward. This way the elements that risks having their key changed during the loop are the elements we already checked.
Note that each call to splice has generally O(n) complexity. There are many ways to instead achieve your desired result an order of magnitude more efficiently with a single O(n) iteration. Here's one:
let a = [0, 1, 2, 0, 0, 3, 0]
for (let i=0, j=0; j<a.length; j++)
if (a[j] && i != j)
[a[i++], a[j]] = [a[j], 0]
console.log(a)

Loop not looping inside array & comparing elements of an array

In this function I am trying loop into an array, and then return the following thing: if the year is formed by all different digits, then it's a happy year and should be stored where it belongs, in the happy array. If not, it should go in the notHappy array.
Now the problems:
1) The IF condition I tried returns nothing, []. I am quite sure it's not the right way of doing it.
for (var i = 0; i <= t.length; i++) {
if (i[0] != i[1] && i[0] != i[2] && i[0] != i[3]) {
o.happy.push(i++);
} else {
o.notHappy.push(i++)
}
}
2) I tried the same loop with a simple i%2 === 0 condition and I found out that the loop ignores my arr altogether and returns [0, 2, 4] instead of the actual numbers. It's like it would start looping from 0 itself. How come?
function nextHappyYear(arr){
var o = {
happy: [],
notHappy: []
};
var t = arr.map(e => e.toString().split(""));
for (var i = 0; i <= t.length; i++) {
if (i%2 === 0) {
o.happy.push(i++);
} else { o.notHappy.push(i++)}
}
return console.log(o.happy)
}
nextHappyYear([1021, 1022, 1023, 1024]) // returns [0, 2, 4] instead of [1022, 1024]
Your code has some issues
1-for (var i = 0; i <= t.length; i++)
Arrays indexes start from 0 and ends with length - 1. So your condition i <= t.length makes an error. Change it to i < t.length.
2-if (i%2 === 0)
This is not your question condition. You must get all digits in a year and check equality of them.
3-o.happy.push(i++);
This part have 2 problem. First, you push into happy array the index of that year, not the year. Second, i++ increase i by one and one year will get skipped!
4-if (i[0] != i[1] && i[0] != i[2] && i[0] != i[3])
You check just the first digit with others and you don't check second with third and fourth, third digit with forth also.
Try this
function hasDuplicate(arr) {
arr.sort()
for (var i = 0; i < arr.length - 1; i++) {
if (arr[i + 1] == arr[i]) {
return true;
}
}
return false;
}
function happyYear1(arr) {
var o = {
happy: [],
notHappy: []
};
for (var i = 0; i < arr.length; i++) {
if (!hasDuplicate((arr[i] + '').split(""))) {
o.happy.push(arr[i]);
} else {
o.notHappy.push(arr[i]);
}
}
return o;
}
var output = happyYear1([1021, 1022, 1023, 1024]); // returns [0, 2, 4] instead of [1022, 1024]
console.log(output);

Categories

Resources