How do I turn this recursion into tail recursion? - javascript

With given array on unique numbers which are always greater than 0 I need to find all possible unique combinations of those numbers that are equal to a certain number when summed.
For example, getNumberComponents([7, 4, 3, 2, 5, 6, 8, 1], 8) should return
[ [ 7, 1 ], [ 4, 3, 1 ], [ 3, 5 ], [ 2, 5, 1 ], [ 2, 6 ], [ 8 ] ] because sum of all numbers in every subarray equals 8.
My solution:
function getNumberComponents(numArray, number) {
const arrayLength = numArray.length;
const allVariants = [];
function findComponents(currentIndex = 0, currentVariant = []) {
while (currentIndex < arrayLength) {
const currentElement = numArray[currentIndex];
const currentSum = currentVariant.reduce((acc, cur) => acc + cur, 0);
const sumWithCurrent = currentSum + currentElement;
if (sumWithCurrent === number) {
allVariants.push([...currentVariant, currentElement]);
}
currentIndex++;
if (sumWithCurrent < number) {
findComponents(currentIndex, [...currentVariant, currentElement]);
}
}
}
findComponents();
return allVariants;
}
But I wonder if it's possible to use tail recursion for that? I have no idea how to turn my solution into tail recursion.

To make this tail recursive, you could:
Keep track of all indices that were selected to arrive at the current sum. That way you can easily replace a selected index with the successor index.
In each execution of the function get the "next" combination of indices. This could be done as follows:
If the sum has not been achieved yet, add the index the follows immediately after the most recently selected index, and adjust the sum
If the sum has achieved or exceeded, remove the most recently selected index, and then add the successor index instead, and adjust the sum
If there is no successor index, then forget about this index and replace the previous one in the list, again adjusting the sum
If there are no more entries in the list of indices, then all is done.
Instead of accumulating a sum, you could also decrease the number that you pass to recursion -- saving one variable.
Make the function return the array with all variants, so there is no need for an inner function, nor any action that follows the function call.
Here is an impementation:
function getNumberComponents(numArray, number, selectedIndices=[], allVariants=[]) {
let i = selectedIndices.at(-1)??-1;
if (number < 0) { // Sum is too large. There's no use to adding more
i = numArray.length; // Force the while-condition to be true
} else if (number == 0) { // Bingo
allVariants.push(selectedIndices.map(idx => numArray[idx]));
}
while (++i >= numArray.length) { // No more successor index available
if (selectedIndices.length == 0) return allVariants; // All done
i = selectedIndices.pop(); // Undo a previous selection
number += numArray[i]; // Remove from sum
}
selectedIndices.push(i); // Select index and recur:
return getNumberComponents(numArray, number - numArray[i], selectedIndices, allVariants);
}
console.log(getNumberComponents([7, 4, 3, 2, 5, 6, 8, 1], 8));

Here is my version of your function, but using tail recursion. This is still a complex subject for me, check if there are no mistakes
function getNumberComponents(numArray, number, currentIndex = 0, currentVariant = [], allVariants = new Set()) {
if (currentIndex >= numArray.length) {
const currentSum = currentVariant.reduce((acc, cur) => acc + cur, 0);
if (currentSum === number) {
allVariants.add(currentVariant);
}
return Array.from(allVariants);
}
const currentElement = numArray[currentIndex];
const currentSum = currentVariant.reduce((acc, cur) => acc + cur, 0);
const sumWithCurrent = currentSum + currentElement;
if (sumWithCurrent <= number) {
allVariants = new Set([...allVariants, ...getNumberComponents(numArray, number, currentIndex + 1, [...currentVariant, currentElement], allVariants), ...getNumberComponents(numArray, number, currentIndex + 1, currentVariant, new Set())]);
} else {
allVariants = new Set([...allVariants, ...getNumberComponents(numArray, number, currentIndex + 1, currentVariant, new Set())]);
}
return Array.from(allVariants);
}
console.log(getNumberComponents([7, 4, 3, 2, 5, 6, 8, 1], 8));

Related

Get 5 closest elements to an element in array including that element

I am trying to get 5 closest elements to given element, including that element, in array. For example, if we have:
const arr = [1, 2, 3, 4, 7, 11, 12, 13, 15, 17]
and I want to get 5 closest elements to 11, it should return [4, 7, 11, 12, 13]. If i pass 1 it should return [1, 2, 3, 4, 7]. If I pass 15 it should return [11, 12, 13, 15, 17].
I'm not sure what you meant;
You might've meant a code to find the element and return the five nearest elements to it by place in the array;
Or you might've meant a code to find the 5 numbers closest to a number you say.
IF you meant the first case
There are two ways to do so,
A value as a parameter
Use this code:
function closestNByValue(arr, value, n) {
let ind = arr.indexOf(value);
let finalArr = [];
if (n > arr.length) {
finalArr = Array.from(arr);
} else if (ind == -1) {
finalArr = [];
} else if (ind <= n/2 - 0.5) {
finalArr = arr.slice(0, n);
} else if (ind >= (arr.length - n/2) - 0.5) {
finalArr = arr.slice(-n);
} else if (n%2 == 0) {
finalArr = arr.slice(ind-(n/2), ind+(n/2));
} else {
finalArr = arr.slice(ind-(n/2 - 0.5), ind+(n/2 + 0.5));
}
return finalArr;
}
console.log(closestNByValue([1, 2, 3, 4, 7, 11, 12, 13, 15, 17], 11, 5))
How does it do the job?
Okay first we need to find the index of the value and save it in ind (short form of 'index') and we check multiple different situations for what the ind is so we'd be able to output the best answer as finalArr.
There are two exceptions; what if there was no such value in our array? then ind = -1 and we'd return an empty array; or what if the number of elements nearby that we seek is larger than or equal to the arr.length? then we'd return all of the arr.
But if there were no exceptions, there are three different situations for the ind; first, ind is a number that makes us have all of the finalArr values from the first of arr, second, ind be a number that makes us have all of the finalArr values from the last of arr, and third, ind being a number that we have to select half from the indexes smaller than ind and half, larger.
If it is the third way, the way we select we'd be different depending on the oddity of the numbers we want to select.
And we'll have a conditional statement for each situation and return the finalArr.
An index as a parameter
function closestNByIndex(arr, ind, n) {
let finalArr = [];
if (n > arr.length) {
finalArr = Array.from(arr);
} else if (ind == -1) {
finalArr = [];
} else if (ind <= n/2 - 0.5) {
finalArr = arr.slice(0, n);
} else if (ind >= (arr.length - n/2) - 0.5) {
finalArr = arr.slice(-n);
} else if (n%2 == 0) {
finalArr = arr.slice(ind-(n/2), ind+(n/2));
} else {
finalArr = arr.slice(ind-(n/2 - 0.5), ind+(n/2 + 0.5));
}
return finalArr;
}
console.log(closestNByIndex([1, 2, 3, 4, 7, 11, 12, 13, 15, 17], 5, 5))
Similar to the first code it works, though we have the index and we don't search for it.
The point is, if you use the function with value, it'd do the nearest 5 elements of the first value that equals the entry but such confusion is not being tolerated in the second code.
IF you meant the second case
This is a code I coded:
const arr = [1, 2, 3, 4, 7, 11, 12, 13, 15, 17];
function allDiff(arr, num1, num2) {
const finalArr = [];
const x = Math.abs(num2 - num1);
for (let y = 0; y < arr.length; y++) {
if (Math.abs(arr[y] - num1) == x) {
finalArr.push(arr[y]);
}
}
return finalArr;
}
function deleteArr(arr, delet) {
for (let x = 0; x < arr.length; x++) {
if (delet.includes(arr[x])) {
delete arr[x];
}
}
return arr;
}
function closest(arr, num) {
const map = new Map()
arr2 = Array.from(arr);
let key, value;
for (let x = 0; x < arr2.length; x++) {
key = Math.abs(arr2[x] - num);
value = allDiff(arr2, num, arr2[x]);
arr2 = deleteArr(arr2, value);
map.set(key, value);
}
return map;
}
function closestN(arr, num, n) {
const map = closest(arr, num);
const mapKeys = Array.from(map.keys());
const mapKeysSorted = mapKeys.sort(function(a, b) {
return a - b
});
let finalArr = [];
let y;
for (let i = 0; i < mapKeysSorted.length; i++) {
if (n <= 0) {
break;
}
y = map.get(mapKeysSorted[i]);
if (n < y.length) {
finalArr = finalArr.concat(y.slice(0, n + 1));
break;
}
finalArr = finalArr.concat(y);
n -= y.length;
}
return finalArr;
}
console.log(closestN(arr, 11, 5));
It might be a little too long, but I have programmed it as you can give it any array (arr) with integer values, an integer (num) that you'd like it to be the base and another integer (n) for the number of the size of the output array, 5 in this case.
Explaining the code
The function closest would return a map of (the difference between the numbers, a list of the numbers in the arr that differs the number equal to their key).
The main function, closestN, calls the closest function and saves the map in the map variable.
Then it sorts the keys of the map in mapKeysSorted.
Now, a for loop loops through the mapKeySorted array and pushes new elements to the finalArr until the size of the finalArr reaches the number of elements we seek.
The main function is the closestN.
Here's a way to get to your goal:
To start, first thing to do is finding the index of the wanted number in the array. Example index of 1 in your array arr is 0. The index will help in extracting the numbers later on. The method findIndex will help us in finding the index.
Then, we need to find the position at which will start extaracting the closest numbers (in terms of position not value). As seen from the desired output you have provided, usually you want the returned array to be in the following structure:
output: [
2 nearest numbers (from N left),
the wanted number,
2 nearest numbers (from N right)
]
This can get tricky so we should make sure to deal with some edge case like when the wanted element is sitting at position 0.
Extract the numbers and return them as an array as described by your desired output. The use of slice method will come in handy here which allow us to extract the numbers just as we need.
Here's a live demo demonstrating solution:
const arr = [1, 2, 3, 4, 7, 11, 12, 13, 15, 17],
/** a function that returns an array containing the "5" (depending on "arr" length that could be less) nearest numbers (in terms of position) in "arr" array to the supplied number "n" */
findClosestNumbers = n => {
/** make sure we don't exceed the array length */
const toTake = 5 > arr.length ? arr.length : 5,
/** find the index of the wanted nulber "n", if "-1" is returned then "n" cannot be found ion the array "arr" */
idx = arr.findIndex(el => n == el),
/**
* from where we should start returning the nearest numbers (the position of the first number to extract from "arr"
* the below condition help deal with some edge cases like when "n" is the last element in "arr"
*/
startIdx = idx + toTake / 2 > arr.length ?
arr.length - 5 :
(idx - 2 >= 0 ?
idx - 2 :
0);
/** return the nearest numbers or return an empty array "[]" if the number "n" is not found on the array "arr" */
return idx == -1 ? [] : arr.slice(startIdx, startIdx + 5);
};
/** run for various scenarios */
console.log('For 1 =>', findClosestNumbers(1));
console.log('For 11 =>', findClosestNumbers(11));
console.log('For 15 =>', findClosestNumbers(15));
console.log('For 17 =>', findClosestNumbers(17));
.as-console-wrapper {
max-height: 100%!important;
}
The demo above is meant to help you understand how things could work and it is not the only way to get to your goal. Also, because I kept it as simple as possible, the above demo is wide open for improvements.

How do i multiply every number in the array from position tabProsent[I] to the left?

How do i multiply every number in the array from position tabProsent[I] to the left? It is not working for me to write tabProsent[i].reduceRight(getSum).
function getSum(total, num) {
return total * num;
}
// Update the current slider year (each time you drag the slider handle)
slider.oninput = function() {
output.innerHTML = this.value;
var i;
for (i = 0; i < tabAar.length; i++) {
if (slider.value == tabAar[i]) {
output2.innerHTML=Math.round(startVerdi * tabProsent[i].reduceRight(getSum));
}
}
}
This is a possible solution.
Note: I don't know what the result should be if the index is negative, actually then you get an result of 1. If index is greater then array-length the I reduce pos to the array-length.
function getMult(arr, pos) {
if (pos>=arr.length)
pos = arr.length-1;
let res =1;
for (let i=0; i<= pos; i++) {
res *= arr[i];
}
return res;
}
console.log(getMult([1,2,3,4,5], 3));
A functional approach could look like this:
let multToLeft = (arr, num, m) => {
if (num < 0 || num > arr.length) throw new Error(`Invalid num ${num}`);
return [ ...arr.slice(0, num).map(v => v * m), ...arr.slice(num) ];
};
let examples = [
[ [ 1, 2, 3, 4, 5 ], 2, 10 ],
[ [ 1, 2, 3, 4, 5 ], 3, 20 ],
[ [ 1, 2, 3, 4, 5 ], 5, 11 ]
];
for (let [ arr, num, m ] of examples) {
console.log(`multToLeft(${JSON.stringify(arr)}, ${num}, ${m}) -> ${JSON.stringify(multToLeft(arr, num, m))}`);
}
Note that I am working with num instead of index. I've chosen num to indicate the actual number of elements to the left of the array that get multiplied. This makes more sense than working with index, as it becomes messy to apply the multiplication to zero elements in the array (since supplying 0 would still mean to apply the multiplication to the first array item).

find how many elements from the beginning of the array must be added to get a total of more than 10

I have an array with numbers. Need to find how many elements from the beginning of the array must be added to get a total of more than 10. In the code I have to use reduce
    let arr = [2, 3, 1, 0, 4, 5, 4];
The console in this case must display the number 6.
My code didn't work:
let arr = [2, 3, 1, 0, 4, 5, 4];
let sumNumber = arr.reduce((sum, elem, index) => {
let ourSum = 0;
while (ourSum <= 10) {
return sum + elem;
}
index++
}, 0)
console.log(sumNumber);
You could find the index and add one.
This iteration stops if the comparison is true.
let array = [2, 3, 1, 0, 4, 5, 4],
index = array.find((sum => value => (sum += value) > 10)(0));
console.log(index + 1);
let arr = [2, 3, 1, 0, 4, 5, 4];
// i is to store the element count at which it gets greater than 10
let i=0;
let sumNumber = arr.reduce((sum, elem, index) => {
// Checks if sum is greater than 10 yet?
if(sum>=10){
// this condn below will only evaluate once when for the
// first time the sum is greater than or equal to 10
if(i===0) i=index+1;
}
// updates the sum for every loop run
return sum+elem;
});
console.log("Element count for which sum is greater than equal to 10 is:",i);
This is one solution:
const arr = [2, 3, 1, 0, 4, 5, 4]
const sumNumber = arr.reduce(
(acc, cur, index) => {
if (typeof acc === 'number') { // If the accumulator is a number...
return acc // Just return it
}
const sum = acc.sum + cur
if (sum > 10) { // If sum is greater than 10...
return index + 1 // ...return a number
}
return { sum } // Return an object with sum with the new value
},
{ sum: 0 } // Start with an object with the `sum` property of 0
)
console.log(sumNumber)
You can do this with some method, you do not have to loop through the rest of the elements when condition is achived.
var arr = [2, 3, 1, 0, 4, 5, 4];
var obj = {sum : 0, index : 0 };
arr.some((k,i)=>{
obj.sum+=k;
if(obj.sum > 10) {
obj.index = i+1;
return true
}
});
console.log(obj)

How to find and count duplicate integers in an array with javascript

Hello I am taking an array of integers with ranging numbers from 1 - 100 and I'm counting the duplicated numbers within it. Example, array[1,1,1,1,1,100,3,5,2,5,2,23,23,23,23,23,]. Result = 1 - 5 times, 5 - 2 times, 2 - 2 times, 23 - 5 times. I cannot see how to make this work I have tried to edit this code snippet so that it counts and returns the number of duplicates of a specific integer that is a duplicate but I could not see how to do it. Please assist Thank You.
https://repl.it/#youngmaid/JS-ALGORITHMS-Counting-Duplicates
//To count or reveal duplicates within an array. Using the array method of sort() is one way.
//Sort the following array using .sort(), which put the items in the array in numerical or alphabetical order.
//Create a new variable for the sorted array.
//Also create a new variable for an empty array.
//Create a loop using the length of the first, original array with an increment of "++".
//Create an if statement that includes adding an item comparing to the index.
//Then push the emply array in the sorted array.
//console log the new array.
let duplicateArr = [5, 3, 7, 4, 7, 5, 3, 2, 7, 3, 2];
let sortArr = duplicateArr.sort();
let newArr = [];
for(let i = 0; i < duplicateArr.length; i++) {
if(sortArr[i + 1] == sortArr[i]){
newArr.push(sortArr[i]);
}
}
console.log(newArr);
//The other way or more detailed/reusable approach is to create a function and variable hash table.
//The hash table to place all the items in the array.
//Then create another variable placing duplicates in the array.
//Then go through each item in the array through a for loop. (Using arr as the argument).
//Create a conditional if/else statement. If the item in the hash table does not exist, then insert it as a duplicate.
function duplicates(arr) {
let hashTable = [];
let dups = [];
for (var i = 0; i < arr.length; i++){
if (hashTable[arr[i].toString()] === undefined) {
hashTable[arr[i].toString()] = true;
} else {
dups.push(arr[i]);
}
}
return dups;
}
duplicates([3, 24, -3, 103, 28, 3, 1, 28, 24]);
If I understand correctly, you could achieve this via Array#reduce() as shown below:
let duplicateArr = [5, 3, 7, 4, 7, 5, 3, 2, 7, 3, 2];
/* Reduce the input duplicateArr to a map relating values to counts */
const valueCounts = duplicateArr.reduce((counts, value) => {
/* Determine the count of current value from the counts dictionary */
const valueCount = (counts[ value ] === undefined ? 0 : counts[ value ])
/* Increment count for this value in the counts dictionary */
return { ...counts, ...{ [value] : valueCount + 1 } }
}, {})
/* Remove values with count of 1 (or less) */
for(const value in valueCounts) {
if(valueCounts[value] < 2) {
delete valueCounts[value]
}
}
/* Display the values and counts */
for(const value in valueCounts) {
console.log(`${ value } occours ${ valueCounts[value] } time(s)` )
}
Reasonably basic loop approach
const data = [1, 1, 1, 1, 1, 100, 3, 5, 2, 5, 2, 23, 23, 23, 23, 23, ]
function dupCounts(arr) {
var counts = {};
arr.forEach(function(n) {
// if property counts[n] doesn't exist, create it
counts[n] = counts[n] || 0;
// now increment it
counts[n]++;
});
// iterate counts object and remove any that aren't dups
for (var key in counts) {
if (counts[key] < 2) {
delete counts[key];
}
}
return counts
}
console.log(dupCounts(data))
Here using only 1 loop.
let duplicateArr = [5, 3, 7, 4, 7, 5, 3, 2, 7, 3, 2]
let sortArr = duplicateArr.sort()
let current = 0, counter = 0
sortArr.forEach(n => {
if (current === n) {
counter++
}
else {
if (counter > 1){
console.log(current + " occurs " + counter + " times.")
}
counter = 1
current = n
}
})
if (counter > 1){
console.log(current + " occurs " + counter + " times.")
}
The cleanest way is using ES6 Map
function duplicates(arr) {
// This will be the resulting map
const resultMap = new Map();
// This will store the unique array values (to detect duplicates using indexOf)
const occurrences = [];
for (let i of arr){
if (occurrences.indexOf(i) !== -1) {
// Element has a duplicate in the array, add it to resultMap
if (resultMap.has(i)) {
// Element is already in the resultMap, increase the occurrence by 1
resultMap.set(i, resultMap.get(i) + 1);
} else {
// Element is not in resultMap, set its key to 2 (the first 2 occurrences)
resultMap.set(i, 2);
}
} else {
// Element is showing for the first time (not a duplicate yet)
occurrences.push(i);
}
}
return resultMap;
}
// To iterate on the map keys and values use this
for (const [key, value] of map) {
console.log(key + ' - ' + value + ' times');
}
You can just iterate over all of the unique values and then count how many of them exists.
here is a sample code:
let duplicateArr = [5, 3, 7, 4, 7, 5, 3, 2, 7, 3, 2];
let sortArr = duplicateArr.sort();
let newArr = {};
let duplicateValues = [];
for (let i = 0; i < duplicateArr.length; i++) {
let count = 0;
let k = 0;
while (i + k < duplicateArr.length && sortArr[i] == sortArr[i + k]) {
count++;
k++;
}
if (count > 1) {
newArr[sortArr[i]] = count;
duplicateValues.push(sortArr[i]);
}
i = i + k;
}
console.log("duplicate items with count:", newArr);
console.log("duplicate items:", duplicateValues);
Using Array.prototype.reduce() you can create a hash object variable containing as keys the numbers in the duplicateArr array variable and the values are the number of repeated times..
Code:
const duplicateArr1 = [5, 3, 7, 4, 7, 5, 3, 2, 7, 3, 2];
const duplicateArr2 = [1, 1, 1, 1, 1, 100, 3, 5, 2, 5, 2, 23, 23, 23, 23, 23];
const getStringOfDuplicated = array => {
const hash = array.reduce((a, c) => (a[c] = ++a[c] || 1, a), {});
return Object.entries(hash)
.filter(([k, v]) => v > 1)
.sort(([ak, av], [bk, bv]) => bv - av)
.map(([k, v]) => `${k} - ${v} times`)
.join(', ');
};
console.log(getStringOfDuplicated(duplicateArr1));
console.log(getStringOfDuplicated(duplicateArr2));

Target array elements using forEach

This function multiplies array numbers with their neighbor and returns the largest product. I tried making this function using forEach, but I couldn't get the inputArray[i + 1] to work. How can I target an array elements neighbor in a forEach? Thanks!
function adjacentElementsProduct(inputArray) {
var product = inputArray[0] * inputArray[1];
for(var i = 0; i < inputArray.length; i++) {
if((inputArray[i] * inputArray[i + 1]) > product) {
product = inputArray[i] * inputArray[i + 1];
}
}
console.log(product);
}
You could take a copy of the given array, starting by the second element with Array#slice and iterate over that array. Then take the product of the same index of the iterating array and the original array for a product. Compare and update product if necessary.
How it works:
index 0 1 2 3 4 5 6 7
original [ 1, 3, 5, 3, 7, 4, 8, 2 ]
copy [ 3, 5, 3, 7, 4, 8, 2 ]
product 3 15 15 21 28 32 16
function adjacentElementsProduct(inputArray) {
var product = inputArray[0] * inputArray[1];
inputArray.slice(1).forEach(function (a, i) {
var p = inputArray[i] * a;
if (p > product) {
product = p;
}
});
console.log(product);
}
adjacentElementsProduct([1, 3, 5, 3, 7, 4, 8, 2]);
The same with Array#reduce
function adjacentElementsProduct(array) {
return array.slice(1).reduce(function (r, a, i) {
var p = array[i] * a;
return r >= p ? r : p;
}, undefined);
}
console.log(adjacentElementsProduct([1, 3, 5, 3, 7, 4, 8, 2]));
console.log(adjacentElementsProduct([-1, 1]));
console.log(adjacentElementsProduct([0, 0]));
console.log(adjacentElementsProduct([42]));
console.log(adjacentElementsProduct([]));
foreach arguments provide current index and array object. There is no way to directly get next value in arguments.
inputArray.forEach(function(currentValue, index, array){
product = currentValue * array[index+1]
});
As described in the documentation, the foreach callback function takes 3 parameters :
the element value
the element index
the array being traversed
so you can access to the next item like that:
myArray.forEach(function(value, index, array) {
product = value * array[index + 1];
}
I let you complete your code to get the higher product ;)
forEach's callback function takes three parameters - the current item, the current index, and the array.
A conversion of your code to forEach would look like this:
function adjacentElementsProduct(inputArray) {
let product = inputArray[0] * inputArray[1];
inputArray.forEach((item, index, array) => {
if (item * array[index + 1] > product) {
product = item * array[index + 1];
}
});
console.log(product);
}
In my opinion reduce is a better fit for this type of problem.
function adjacentElementsProduct(inputArray) {
const result = inputArray.reduce((largestSoFar, current, index, array) => {
const product = current * array[index + 1];
return (largestSoFar === null || product > largestSoFar) ?
product :
largestSoFar;
}, null);
console.log(result);
}

Categories