Three Number Sum - Finding triplets that sum to target - javascript

Write a function that takes in a non-empty array of distinct integers and a target integer.
Your function should find all triplets in the array that sum up to the target sum and return a two-dimensional array of all these triplets.
Each inner array containing a single triplet should have all three of its elements ordered in ascending order
ATTEMPT
function threeNumberSum(arr, target) {
let results = [];
for (let i = 0; i < arr.length; i++) {
let finalT = target - arr[i];
let map = {};
for (let j = i+1; j < arr.length; j++) {
if (map[arr[j]]) {
results.push([arr[j], arr[i], map[arr[j]]]);
} else {
map[finalT-arr[j]] = arr[j];
}
}
}
return results;
}
My code is formatted all funny, but right now im not getting any output. Am I missing a console log somewhere or something?

Your problem is that you read input wrong.
Pay attention to the last part of question: How to Read Input that is Used to Test Your Implementation
You wrote a function that takes the array as first arg and the target integer as the second one. But the input is entered one by one, so your program should read one value at a time from the console input.

Related

Create array with function javascript

I need to create function that creates and returns array. Its size needs to match the rows parameter, and each next element contains consecutive integers starting at 1. To call this function I need to use argument 5. Here below is what I wrote so far. Can you tell me what's wrong here?
function createArray(rows) {
for(let i = 1; i < rows.length; i++) {
console.log(rows[i]);
}return rows;
}
createArray(5);
You need to create an array and return it, whereas you return just rows which is a number. The idea of using a for loop is the best way to go. In that loop you just need to set the values in the array accordinlgy.
Another problem in your code is that rows is of type number and does have a property length but that does not have the desired value. So we just use rows in the for loop. We start the loop with i = 0 because array indices start at 0.
Code
function createArray(rows) {
let arr = new Array(rows);
for (let i = 0; i < rows; i++) {
arr[i] = i + 1;
}
return arr;
}
console.log(createArray(5));
We can not use length property for number. create an empty array and then push values into that array until required size is achieved.
function createArray(rows) {
var arr = [];
for(let i = 1; i <= rows; i++) {
arr.push(i);
}return arr;
}
createArray(5);
I think what you want is createArray(5) return [1,2,3,4,5] if that's the case you could do this
function createArray(rows) {
const arr = []
for(let i = 1; i <= rows; i++) {
arr.push(i);
}
return arr;
}
console.log(createArray(5));
The problem is, that rows.length is not available on 5, because 5 is a number.
You have to use an array as parameter:
Array(5) creates an array with the length of 5 and fill("hello") fills this array with "hello" values.
function createArray(rows) {
for (let i = 1; i < rows.length; i++) {
console.log(rows[i]);
}
return rows;
}
const rows = Array(5).fill("hello");
createArray(rows);
I don't know, if this is the behaviour you want, if not, I misunderstood your question.

Finding target sum of three numbers - optimizing nested loops

I have a problem where, given an array of integers, I need to find sets of three numbers that add up to equal zero. The below solution works but isn't as optimal as I'd like and I am looking for ways to optimize it to avoid unnecessary processing.
What I am doing below is I am iterating through the all combinations of numbers while eliminating iterating through the same indices in each nested loop and I am checking if the three numbers in the inner most loop add up to zero. If yes, I am converting the array to a string and if the string isn't already in the results array I am adding it. Right before returning I am then converting the strings back to an array.
I appreciate any suggestions on how to further optimize this or if I missed out on some opportunity to implement better. I am not looking for a total refactor, just some adjustments that will improve performance.
var threeSum = function(nums) {
const sorted = nums.sort();
if(sorted.length && (sorted[0] > 0 || sorted[sorted.length-1] < 0)) {
return [];
}
let result = [];
for(let i=0; i < sorted.length; i++) {
for(let z=i+1; z < sorted.length; z++) {
for(let q=z+1; q < sorted.length; q++) {
if(sorted[i]+sorted[z]+sorted[q] === 0) {
const temp = [sorted[i], sorted[z], sorted[q]].join(',');
if(!result.includes(temp)) {
result.push(temp);
}
}
}
}
}
return result.map(str => str.split(','));
};
Sample Input: [-1,0,1,2,-1,-4]
Expected Output: [[-1,-1,2],[-1,0,1]]
One obvious optimisation is to precalculate the sum of the two first numbers just before the third nested loop. Then compare in the third loop if that number equals the opposite of the third iterated number.
Second optimisation is to take advantage of the fact that your items are sorted and use a binary search for the actual negative of the sum of the two first terms in the rest of the array instead of the third loop. This second optimisation brings complexity from O(N3) down to O(N2LogN)
Which leads to the third optimisation, for which you can store in a map the sum as key and as value, an array of the different pairs which sum to the sum so that each time you want to operate the binary search again, first you check if the sum already exists in that map and if it does you can simply output the combination of each pair found at that sum’s index in the map coupled with the negative sum.
The OP's solution runs in O(N³) time with no additional storage.
The classic "use a hash table" solution to find the missing element can bring that down to O(N²) time with O(N) storage.
The solution involves building a number map using an object. (You could use a Map object as well, but then you can't be as expressive with ++ and -- operators). Then just an ordinary loop and inner loop to evaluate all the pairs. For each pair, find if the negative sum of those pairs is in the map.
function threeSum(nums) {
var nummap = {}; // map a value to the number of ocurrances in nums
var solutions = new Set(); // map of solutions as strings
// map each value in nums into the number map
nums.forEach((val) => {
var k = nummap[val] ? nummap[val] : 0; // k is the number of times val appears in nummap
nummap[val] = k+1; // increment by 1 and update
});
// for each pair of numbers, see if we can find a solution the number map
for (let i = 0; i < nums.length; i++) {
var ival = nums[i];
nummap[ival]--;
for (let j = i+1; j < nums.length; j++) {
var jval = nums[j];
nummap[jval]--;
var target = -(ival + jval); // this could compute "-0", but it works itself out since 0==-0 and toString will strip the negative off
// if target is in the number map, we have a solution
if (nummap[target]) {
// sort this three sum solution and insert into map of available solutions
// we do this to filter out duplicate solutions
var tmp = [];
tmp[0] = ival;
tmp[1] = jval;
tmp[2] = target;
tmp.sort();
solutions.add(tmp.toString());
}
nummap[jval]++; // restore original instance count in nummap
}
nummap[ival]--;
}
for (s of solutions.keys()) {
console.log(s);
}
}
threeSum([9,8,7,-15, -9,0]);
var threeSum = function(unsortedNums) {
const nums = unsortedNums.sort();
if(nums.length && (nums[0] > 0 || nums[nums.length-1] < 0)) {
return [];
}
const result = new Map();
for(let i=0; i < nums.length; i++) {
for(let z=i+1; z < nums.length; z++) {
for(let q=z+1; q < nums.length; q++) {
if(nums[i]+nums[z]+nums[q] === 0) {
const toAdd = [nums[i], nums[z], nums[q]];
const toAddStr = toAdd.join(',');
if(!result.has(toAddStr)) {
result.set(toAddStr, toAdd);
}
}
}
}
}
return Array.from(result.values());
};

Loop thought a multi dimensional array

I try to return a multidimensional array into a function to iterate it but I'm not sure what's wrong with my logic
const arr = [[1,2], [3,4],[5,6]]
for(let i = 0; i < thirdInterval.length-1; i++){
getNumbers(thirdInterval[i], thirdInterval[i+1])
}
The result that I want to achieve is return the first element into the first argument of the function and the second element of the array into the second argument of the function.
What you are doing here is looping through the array and getting only the array at the index i, e.g arr[0] which is [1,2]. and (thirdInterval[i], thirdInterval[i+1]) is actually equals to ([1,2], [3,4])
to access the first and second elements you should address them like the following:
for(let i = 0; i < thirdInterval.length-1; i++){
getNumbers(thirdInterval[i][0], thirdInterval[i][1])
}
const arr = [[1,2][3,4][5,6]];
for (var i = 0; i < arr.length; i++;) {
func(arr[i][0], arr[i][1];
}
You are iterating an array with sub-arrays, which means that thirdInterval[i] contains two items. You can get the items using the indexes thirdInterval[i][0] and thirdInterval[i][1], but since you're calling a function with those values, you can use spread instead - getNumbers(...thirdInterval[i]).
In addition, the loop's condition should be i < thirdInterval.length if you don't want to skip the last item.
Demo:
const thirdInterval = [[1,2],[3,4],[5,6]]
const getNumbers = console.log // mock getNumbers
for (let i = 0; i < thirdInterval.length; i++) {
getNumbers(...thirdInterval[i])
}

Why should length of the array be stored in selection sort algorithm?

The algorithm code from Grokking algorithm book:
const findSmallestIndex = (array) => {
let smallestElement = array[0]; // Stores the smallest value
let smallestIndex = 0; // Stores the index of the smallest value
for (let i = 1; i < array.length; i++) {
if (array[i] < smallestElement) {
smallestElement = array[i];
smallestIndex = i;
}
}
return smallestIndex;
};
// 2. Sorts the array
const selectionSort = (array) => {
const sortedArray = [];
const length = array.length;
for (let i = 0; i < length; i++) {
// Finds the smallest element in the given array
const smallestIndex = findSmallestIndex(array);
// Adds the smallest element to new array
sortedArray.push(array.splice(smallestIndex, 1)[0]);
}
return sortedArray;
};
console.log(selectionSort([5, 3, 6, 2, 10])); // [2, 3, 5, 6, 10]
The problem is in the function selectionSort, storing the array length in the variable wes necessary to make it work correctly and this one i couldn't understand, i tried to not store the length in a variable:
const selectionSort = (array) => {
const sortedArray = [];
for (let i = 0; i < array.length; i++) {
// Finds the smallest element in the given array
const smallestIndex = findSmallestIndex(array);
// Adds the smallest element to new array
sortedArray.push(array.splice(smallestIndex, 1)[0]);
}
return sortedArray;
};
console.log(selectionSort([5, 3, 6, 2, 10])); // [2, 3, 5]
I guessed that the problem may be the splice method because it reduces the length every time in the loop but i think the index is not important here, so it may not be be the problem!
Your code is removing the element from the original array, so on each iteration, i++ increases i and also splice decreases array.length. That means i and array.length get closer together by 2 each time instead of by 1, so the loop only iterates half as many times as you want it to. That means you only sort half of the elements into sortedArray.
By copying const length = array.length; first, the variable length is not changed inside the loop, so the i++ makes i closer to length on each iteration by 1, so the number of iterations is the original array length, and every element gets sorted.
As a side note, your algorithm sorts into a new array, but leaves the original array empty. That's probably never what you want; a sorting algorithm should either sort the array in-place (leaving the original array in sorted order), or return a new sorted array (leaving the original array unchanged). You could fix this by making a copy of array at the start of your function, so the algorithm destroys the copy instead of the original.
I'm putting this here for the sole reason that the posted implementation, apparently from an algorithms text, is needlessly overcomplicated and inefficient as well.
function selectionSort(array) {
function smallestIndex(start) {
let si = start;
for (let i = start + 1; i < array.length; ++i) {
if (array[i] < array[si])
si = i;
}
return si;
}
for (let i = 0; i < array.length; i++) {
let index = smallestIndex(i), t;
// swap value into current slot
t = array[index];
array[index] = array[i];
array[i] = t;
}
return array;
}
Here, the smallestIndex() function is enhanced to take a starting position as a parameter. Thus it finds the index of the smallest value in the remainder of the array. On the first iteration, that'll be the smallest value in the whole array. That value is swapped with whatever is at the current starting point, so after that first time through the main loop position 0 in the array is the smallest value in the whole array.
On the next iteration, the search for the index starts at 1, so that process will find the second smallest value from the original array, and swap that into position 1.
The process continues through the array. Note that no new arrays are constructed, and there are no calls to linear-time Array methods.

Range input to function in Google apps script

In a cell in my google sheet, I call my function like this: myFunction(A1:A3), where A1 = 5, A2 = 7 and A3 = 3. I now want to loop over the input (A1, A2, and A3) and (for example) sum them.
function myFunction(input) {
if (!input.map) {
return -1;
}
var sum=0
for(var i=0; i<input.length; i++){
sum = sum + parseInt(input[i]);
}
return sum
}
But it only returns the value in A1 (5) because input.length returns 1.
If I remove parseInt(input[i]), it returns "05,7,3"
What am i doing wrong?
Custom function arguments are converted to JavaScript object types. If the parameter is a multicell range, it's converted to an array object which members are arrays, in other words, as a 2D array.
In order to get each cell value, instead of input[i] use something like input[i][j].
Example:
/**
* #customfunction
*/
function mySum(input) {
if (!input.map) return -1;
var sum = 0;
for(var i = 0; i < input.length; i++){
for(var j = 0; j < input[0].length; j++){
sum = sum + parseInt(input[i][j]);
}
}
return sum
}
Note: The above function could be improved by adding some rules / input data validation, like replace blanks by 0.
References
Custom Functions in Google Sheets
there's built functions for do that but if you want to learn how to script and code this is great !
the code works for just vertical ranges e.g (A1:A5) but in horizontal/square ranges wont, just do another loop inside the first one to do sum of 2D array
for(var i=0; i<input.length; i++){
for(var j=0; j<input[0].length; j++){
sum = sum + parseInt(input[i][j]);
}
}

Categories

Resources