Refactor Nested For Loop - javascript

Instructions
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
How can I refactor this to eliminate the nested for-loop? I'd like to get the time complexity down.
Code
const twoSum = function(nums, target) {
for(let i in nums){
for(let j in nums) {
if(nums[i] + nums[j] === target && nums[i] != nums[j]) {
return [i, j];
}
}
}
};
console.log(twoSum([2, 7, 11, 15], 9));

You can save the difference of each element with the target inside an object with the result as keys and the index as values. This will make checking for the existence of an element inside an object without looping though the whole content. In a different loop check if the array elements exist in the object, if they do then you have got the pair. The additional condition is to prevent comparing an element with itself.
const twoSum = function(nums, target) {
const temp = {};
for(let i=0; i<nums.length; i++) {
temp[target - nums[i]] = i;
}
for(let i=0; i<nums.length-1; i++) {
if(temp[nums[i]] && temp[nums[i]] !== i) {
return [i, temp[nums[i]]]
}
}
};
console.log(twoSum([2, 11, 7, 17], 9));
console.log(twoSum([1, 3, 4, 2], 6));

Since this appears to be homework, I'll make a couple suggestions without giving away a complete solution:
Your current code is repeating index checks. For example, you're looping over indices [0,1] and [1,0], which will always have the same sum since a+b = b+a. Instead, I would suggest your loop for i go from 0 to len-1, and your loop for j go from i+1 to len-1. That way you will never duplicate checks.
Part of your current check includes the condition that nums[i] != nums[j], but your problem doesn't state that two values in the array can't be the same. Is it possible to call this function with values like toSum([1, 4, 4], 8) such that 4+4=8? If so, then you can remove the nums[i] != nums[j] check to save time.
It's not clear if the array provided is sorted. If it's not, then you could create a tracking variable to account for values you've already checked, and prevent checking them on future iterations. For example, if you already compared the value 4 against all other values in the array and found no solution, then if you encounter 4 later in the array, there is no reason to check it.

You can solve this problem with O(n) time. The condition is to solve by this approach is that the array must be sorted.
let twosum = (arr, x) => {
let s = 0,
e = arr.length - 1;
let loc = [];
while (s < e) {
if (arr[s] + arr[e] === x) {
loc.push([s,e]);
s++;
e--;
} else if (arr[s] + arr[e] < x) {
s++;
} else {
e--;
}
}
return loc;
};
console.log(twosum([1, 2, 3, 4, 5, 7, 8], 9));
console.log(twosum([2, 7, 11, 15], 9));
The algorithm behind this if anyone interested:
1. Set s value as 0
2. Set e value as last index say (arr.length - 1)
3. While s is less than e i.e not pass one another
4. Check if arr[s] + arr[e] === x then we find it.
4.1. increment s value by 1 as there is no possibility to find any combination before the current s value
4.2. decrement e value by 1 as there is no possibility to find any combination after the current e value
4.3. collect the indexes where the match found.
5. If arr[s] + arr[e] < x
5.1 increment s as there is no possibility to find any combination before the current s value. But there still has the possibility for the e value to get a match.
6. If arr[s] + arr[e] > x
6.1 decrement e as there is no possibility to find any combination after the current e value. But there still has the possibility for the s value to get a match.

Related

Split array into arrays of numbers where the sum is equal to a specific target

I need to create a function that take as parameter an array and a target. It should return an array of arrays where the sum of these numbers equals to the target
sumPairs(array, target) {
}
For example:
sumPairs([1, 2, 3, 4, 5], 7) // output : [[2, 5], [3, 4]]
I know I have to use map(), and probably reduce(), set(), or filter() maybe (I read their documentation in MDN but still cant find out). I tried some ways but I can't get it.
If you guys could help me to find out how to dynamically create arrays and push them into a new array..
I read there some solutions (Split array into arrays of matching values) but I hate to just use created functions without knowing what they really do or how they work.
Some very basic code for achieving it, Just run all over combinations and conditionally add the items you want.
function sumPairs(array, target) {
var res = [];
for(var i = 0; i < array.length; i++){
for(var j = 0; j < array.length; j++){
if(i!=j && array[i]+array[j]==target &&
res.filter((x)=> x[0] == array[j] && x[1] == array[i]).length == 0 )
res.push([array[i], array[j]]);
}
}
return res;
}
var result = sumPairs([1, 2, 3, 4, 5], 7);
console.log(result);
Option 2 - see this answer for more options (like using reduce)
function sumPairs(array, target) {
return array.flatMap(
(v, i) => array.slice(i+1).filter(w => (v!=w && v+w==target)).map(w=> [w,v])
);
}
var result = sumPairs([1, 2, 3, 4, 5], 7);
console.log(result);
"The exercise says that it sould be arrays of pairs that sum the
target value so I think only 2 items"
If you need a pair that matches a sum and you pick any number from the list, you are left with
the following equation to solve num + x = sum where we want to find x. E.g. if you picked 7 and the target sum is 10 then you know you are looking for a 3.
Therefore, we can first construct a counting map of the numbers available in our list linear (O(n)) time and then search for matches in linear time as well rather than brute forcing with a quadratic algorithm.
const nums = [1, 2, 3, 4, 5];
console.log(findSumPairs(nums, 7));
function findSumPairs(nums, sum) {
const countByNum = countGroupByNum(nums);
return nums.reduce((pairs, num) => {
countByNum[num]--;
const target = sum - num;
if (countByNum[target] > 0) {
countByNum[target]--;
pairs.push([num, target]);
} else {
countByNum[num]++;
}
return pairs;
}, []);
}
function countGroupByNum(nums) {
return nums.reduce((acc, n) => (acc[n] = (acc[n] || 0) + 1, acc), {});
}
Here's another implementation with more standard paradigms (e.g. no reduce):
const nums = [1, 2, 3, 4, 5];
console.log(findSumPairs(nums, 7));
function findSumPairs(nums, sum) {
const countByNum = countGroupByNum(nums);
const pairs = [];
for (const num of nums) {
const target = sum - num; //Calculate the target to make the sum
countByNum[num]--; //Make sure we dont pick the same num instance
if (countByNum[target] > 0) { //If we found the target
countByNum[target]--;
pairs.push([num, target]);
} else {
countByNum[target]++; //Didin't find a match, return the deducted num
}
}
return pairs;
}
function countGroupByNum(nums) {
const countByNum = {};
for (const num of nums) {
countByNum[num] = (countByNum[num] || 0) + 1;
}
return countByNum;
}
You can also sort your array and find all the pairs with given sum by using two pointer method. Place the first pointer to the start of the array and the second pointer to the end.
if the sum of the values at the two places is :
More than target: Decrement your second pointer by 1
Less than target: Increment your first pointer by 1
Equal to target: This is one possible answer, push them to your answer array and increment your first pointer by 1 and decrement your second pointer by 1.
This is more performant solution with complexity O(n*log(n))

Combination Sum in Javascript

I'm trying to create the combination sum algorithm in Javascript.
Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.
The same repeated number may be chosen from candidates unlimited number of times.
My solution is using recursive method.
var combinationSum = function(candidates, target) {
let ans = []
if(candidates === null || candidates.length === 0)
return ans;
candidates.sort();
let current = []
findNumbers(candidates, target, 0, current, ans);
return ans;
};
const findNumbers = function(candidates, target, i, current, ans){
if(target === 0){
const temp = current.slice();
ans.push(temp);
return;
}
for(let j=i; j<candidates.length; j++){
if(target < candidates[j])
return;
current.push(candidates[j]);
findNumbers(candidates, target - candidates[j], j, current, ans);
current.pop();
}
}
It works with basic tests. However, with the input below it fails.
candidates = [3,12,9,11,6,7,8,5,4], target = 15
My output is:
[[3,3,3,3,3],[3,3,3,6],[3,3,4,5],[3,3,9],[3,4,4,4],[3,4,8],[3,5,7],[3,6,6],[4,4,7],[4,5,6],[5,5,5],[6,9],[7,8]]
The correct output should be:
[[3,3,3,3,3],[3,3,3,6],[3,3,4,5],[3,3,9],[3,4,4,4],[3,4,8],[3,5,7],[3,6,6],[3,12],[4,4,7],[4,5,6],[4,11],[5,5,5],[6,9],[7,8]]
I have no clue why it is not inserting in the ans array the solution [3,12] and [4,11]. Any idea why?
Thanks
You are not getting the correct result because you are not sorting the array correctly
By default, sort function sorts the array items by converting them to strings and then comparing their UTF-16 code units, which means your candidates array
[3, 12, 9, 11, 6, 7, 8, 5, 4]
is sorted as
[11, 12, 3, 4, 5, 6, 7, 8, 9]
You need to provide a custom function to sort the numbers correctly
candidates.sort((a, b) => a - b);

How do I find the max in an Array using only math algorithms, specifically I am trying to knock out the lowest number each time

The goal is to get the highest value in an array. I hope someone could walk me through where I went wrong with my code, and how I can get it to work.
What I am trying to accomplish is starting with a preset array, comparing Arr[i] and Arr[i+1], then Arr[i+1] and Arr[i+2] and so on... pushing the highest value of each into an empty array called Arrl. Then set Arr equal to Arrl. Then calling the function back, and repeating until only Arr[0] is left and display the result.
This is the code I have so far:
var Arr=[10,56,76,98,82,45,98];
function largernumber(Arr){
var Arrl=[];
while (Arr.length>1);
for (i=0; i<Arr.length; i++){
if(Arr[i]>Arr[i+1] || Arr[i]===Arr[i+1]){
Arrl.push(Arr[i]);
}
else{
ArrL.push(Arr[i+1]);
}
console.log(Arrl);
}
for(var ar=0 ; ar<Arrl.length ; ar++){
Arr[ar]=Arrl[ar];
}
return largernummber(Arr);
}
I am not great at describing so I will try to show what my plan is.
var Arr=[10,56,76,98,82,45,98]
(after each pass through the array length will decrease by one)
1st pass-through
var Arr=[60,76,98,98,82,98]
2nd
var Arr=[76,98,98,98,98]
3rd
var Arr=[98,98,98,98]
nth..
var Arr=[98]
Currently my logic is so flawed, its freezing the browser. oops... if possible I'd also love to see where I can place the console log to see each iteration.
While all the other answers have pointed out perfectly correct and actually simpler solutions to your problem, I'm just "fixing" your code to use the algorithm that you've mentioned. I've avoided any changes to your code that are unnecessary.
Aside from typos in variable names, these are the issues with the code as it is:
while (Arr.length > 1); just loops forever. I assume it was an attempt to find the base case of the recursion. It should be if (Arr.length === 1) return Arr[0];, which says "if there is only one element left, that is the largest and return it".
When accessing i + 1th element in an array, always be careful about accessing the last element. In your case, your loop goes till i < Arr.length and i + 1 === Arr.length for the last iteration which results in undefined. Comparing numbers with undefined won't work. So make sure you loop till the last but one element, using Arr.length - 1.
Finally, you don't actually use the newly created array for the next leg of the recursion. You need to change return largernumber(Arr); to return largernumber(Arrl);
var Arr = [10, 56, 76, 98, 82, 45, 98];
function largernumber(Arr) {
var Arrl = [];
if (Arr.length === 1) { return Arr[0] };
for (i = 0; i < Arr.length - 1; i++) {
if (Arr[i] > Arr[i + 1] || Arr[i] === Arr[i + 1]) {
Arrl.push(Arr[i]);
} else {
Arrl.push(Arr[i + 1]);
}
}
for (var ar = 0; ar < Arrl.length; ar++) {
Arr[ar] = Arrl[ar];
}
return largernumber(Arrl);
}
console.log(largernumber(Arr));
As you might have noticed by now, point 1 and 3 point out the places where there is infinite recursion.
Just use Math.max(). Given a list of values it returns the max value.
var Arr=[10,56,76,98,82,45,98];
console.log(Math.max(...Arr))
There are several alternatives. The following code snippet uses the function reduce and thru a Math.max you can check for the greatest value.
let arr = [10, 56, 76, 98, 82, 45, 98];
let max = arr.reduce((a, n) => Math.max(a, n), Number.MIN_SAFE_INTEGER);
console.log(max);
The in-build JavaScript Math.max() method can help you achieve your goal as it returns the, erm, maximum value of 2 or more numbers.
It can be used in conjunction with the Array.reduce() method, which applies a function to an array that compares or processes each item down to a single value or property....
var arr = [10,56,76,98,82,45,98];
var max = arr.reduce(function(a, b) {
return Math.max(a, b);
});
// max = 98
Alternatively you can apply() the Math function directly to the array...
var arr = [10,56,76,98,82,45,98];
var max = Math.max.apply(null, arr);
// max = 98
The Array.reduce() method can be useful in many different situations and on arrays which contain different kinds of data. The Math.max.apply() method only works on arrays of numbers.
The in-built Math.min() method works exactly the same, but returns the lowest value of 2 or more numbers.
Hope that helps. ;)
See also:
Array.reduce # MDN
Math.max # MDN
Function.apply # MDN
If what you are trying to do is getting the maximum number in the provided array then you can simply use Math.max() function for that. See below:
const Arr = [10, 56, 76, 98, 82, 45, 98];
const max = Math.max.apply(null, Arr)
console.log(max)
And in the case that you want your code fixed then see below:
const Arr = [10, 56, 76, 98, 82, 45, 98];
function largerNumber(Arr) {
const maximums = [];
// Base case
if (Arr.length == 1) return Arr[0];
// Note the use of Arr.length - 1 instead
for (let i = 0; i < Arr.length - 1; i++) {
if (Arr[i] > Arr[i + 1] || Arr[i] === Arr[i + 1]) {
maximums.push(Arr[i]);
} else {
maximums.push(Arr[i + 1]);
}
}
for (let ar = 0; ar < maximums.length; ar++) {
Arr[ar] = maximums[ar];
}
return largerNumber(maximums);
}
console.log(largerNumber(Arr));

How do I prevent this last "broken condition" from being pushed to array

I am dynamically slicing an array, and I can get the functionality I want by simply removing the last element with arr.pop() but I want to know exactly why my while loop is adding this to my array when it breaks my conditional.
slices(num){
let arr = [this.digits.slice(0, num)]
let i = 0
if (this.digits.length < num){
throw new Error('Slice size is too big.')
} else {
while (arr[i].length === num){
i++
arr.push(this.digits.slice(i, num + i))
}
// arr.pop() - removed for testing
return arr
}
}
Here is an example. Let's say we want to slice this array:
this.digits = [ 3, 1, 0, 0, 1 ]
Ideally, our output will look like this:
[3, 1, 0], [1, 0, 0], [0, 0, 1]]
With my current code and without using arr.pop(), my algorithim will consistently sneak in an extra slice iteration that has less length than what my conditional is asking for (in this case, num == 3)
This will be my output:
[[3, 1, 0], [1, 0, 0], [0, 0, 1], [0, 1]]
I know there are many ways to do this, but for this, I would like to maintain the integrity of my code, so a solution that uses my implementation would be great :D
EDIT: I get why the last element is being added. Since the element before fulfills the conditional (it's length is equal to num), it moves on to the next iteration but how I do handle it eloquently without using .pop()
EDIT: Thanks all for the answers! They all seem like they would work, but Peter B's implementation was so very clean, especially given that he changed just a few lines for me and it worked like a charm. Thanks again!
You are checking the wrong condition in the while. It is better to calculate how many sub-arrays you are going to add inside the while (in addition to the one that you start with), and count up to that number, like this:
var digits = [3, 1, 0, 0, 1];
function slices(num) {
let arr = [this.digits.slice(0, num)]
let i = 0
if (this.digits.length < num) {
throw new Error('Slice size is too big.')
} else {
var sliceCutoff = this.digits.length - num;
while (i < sliceCutoff) {
i++
arr.push(this.digits.slice(i, num + i))
}
return arr
}
}
console.log(slices(3));
You need to check if the leftover items are enough to get an array with the wanted length. That means, you need a single loop with a continuing check for the actual ster index, wanted size and the length.
An approach by checking the last array after splicing is unnecessary, because it generates an avoidable overhead.
function slice(array, n) {
var result = [],
start = 0;
while (start + n <= array.length) {
result.push(array.slice(start, start++ + n));
}
return result;
}
var array = [3, 1, 0, 0, 1];
console.log(slice(array, 3));
Description
I believe that you're looking for something along the lines of this? As you can see, I've also removed some redundant code, i.e. using a while loop in this scenario and the else clause.
I've also just declared digits as a parameter for this demo, I believe that you'd have the initiative to be able to change this to your application(s) requirement(s) without much/any assistance.
function slices(digits, num) {
const arr = [];
if (digits.length < num)
throw new Error('Slice size is too big.')
for (let i = 0; i != num; i++)
arr.push(digits.slice(i, num + i));
return arr;
}
var d = [3, 1, 0, 0, 1]; // Only here for the demo.
console.log(slices(d, 3));
You're really close. I think my proposed solution here keeps the general idea of yours. The problem you're hitting is that checking arr[i].length being equal to num means this is only checking the last item you added to the array, not the next one. Instead, check the item you're about to add.
this.digits = [ 3, 1, 0, 0, 1 ];
function slices(num) {
let arr = []
let i = 0
if (this.digits.length < num) {
throw new Error('Slice size is too big.')
} else {
while (this.digits.slice(i, num + i).length === num){
arr.push(this.digits.slice(i, num + i))
i++
}
// arr.pop() - removed for testing
return arr
}
}
console.log(slices(3));

Simple Recursion in Javascript with/without a subroutine

I'm stuck on how to find the max value of an array recursively in javascript. I tried it first iteratively and of course it works. recursively, I wanted to try it with a subroutine first, and then without a subroutine. What's the best way to call the subroutine inside itself? Where I am getting tripped up is that I want to look inside the indices, but currently my subroutine accepts an array as a parameter.
function maxValue(arr) {
var max = 0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
console.log(maxValue([2, 7, 8, 3, 1, 4])); //returns 8
function maxValueRecursive(arr) {
var length = arr.length; //6
var max = 0;
function doSearch(arr) {
if (arr.length === 1) {
max = arr[0];
} else { // true
max = Math.max(arr[length - 1], arr[length - 2]);
//max = Math.max(4, doSearch()) = 4.
}
return max;
}
return doSearch(arr);
}
console.log(maxValueRecursive([2, 7, 8, 3, 1, 4])) //returns 4
You can use Math.max and solve for a smaller bit of the array at each step. The trick is to remove some (any) item from the array and use Math.max to compare the item with findmax on the smaller array:
function findmax(arr){
if (arr.length == 1){
// base case - single item in array is always max
return arr[0];
}
// recursive call on smaller problem (shorter array)
return Math.max(arr[0], findmax(arr.slice(1)))
}
I used slice but you can use pop or whatever method to remove an item and compare it using Math.max as I did.
For the array [1, 4, 2, 3] the recursion unfolds as follows:
1. findmax([1, 4, 2, 3]
2. Math.max(1, findmax([4, 2, 3]))
3. Math.max(1, Math.max(4, findmax([2, 3])))
4. Math.max(1, Math.max(4, Math.max(2, findmax([3]))))
5. Math.max(1, Math.max(4, Math.max(2, 3))) // 4
For doing it without a subroutine, consider that the maximum of a one-array element is the element itself.
You want to break down the problem such that you can get the input array down to one element, and then start working backwards and checking if there are any larger elements.
Some pseudocode to get you started:
max (A[1, 2, ..., n])
if n = 1
return A[1]
else
oldMax <- max([2, ..., n])
if A[1] > oldMax
return A[1]
else
return oldMax
This may be a bit intimidating at first, but here's an explanation. We know how to get the maximum of an array with one element, so we keep doing the recursive call until we have an array with one element, But here's the trick: the array we pass into the recursive call is the same array, but with the first element removed. This way, we're making the array smaller and smaller until there's only one element left. Then, we know that the maximum is the element itself.
So we get the maximum of the one-element array, and we return that value. Now we're peeling back each layer of recursion as we try to find the biggest value.
Say the array is [1, 2, 3, 4]. Then the function will keep making recursive calls like this:
max([1, 2, 3, 4])
max([2, 3, 4])
max([3, 4])
max([4]) -> 4
At the last stage, we can return 4 since that's the maximum. But now we're back at the stage where the array is [3, 4]. At this stage, we will check to see if the first element, 3, is bigger than the maximum we computed from the recursive call, 4. In this case, it isn't, so we keep returning 4.
We repeat this until we're back at the first layer, where we finally return 4 since 1 is not greater than the maximum so far, 4. Since none of the elements in the array are greater than 4, the maximum never changed.
I know it feels weird at first, but calling a function from within itself is as simple as... well, calling it from within itself!
The below function takes the array as the first parameter, the next two parameters are the indices of the max value at that point in the search and the next value to compare the max value to respectively. You start off by using 0 and 1 and then let the function take over from there.
var findMax = function (array, curMaxIndex, nextIndex) {
if (array[nextIndex] > array[curMaxIndex]) {
curMaxIndex = nextIndex;
}
if (nextIndex > array.length) {
return array[curMaxIndex];
}
return findMax(array, curMaxIndex, nextIndex + 1);
}
var max = findMax([2, 7, 8, 3, 1, 4, 9], 0, 1);
console.log(max); //9
You could also set up default values, so the original call is more simple:
var findMax = function (array, curMaxIndex, nextIndex) {
curMaxIndex = typeof curMaxIndex !== 'undefined' ? curMaxIndex : 0;
nextIndex = typeof nextIndex !== 'undefined' ? nextIndex : 1;
if (array[nextIndex] > array[curMaxIndex]) {
curMaxIndex = nextIndex;
}
if (nextIndex > array.length) {
return curMaxIndex;
}
return findMax(array, curMaxIndex, nextIndex + 1);
}
var max = findMaxIndex(array);
The best way though is to use the built in Math.max(), which it appears you've already heard of, so I'm assuming this is just a learning exercise.

Categories

Resources