Looking for an explanation for a const function in JavaScript - javascript

I was wondering how this worked, and what each part meant.
const factors = number => [...Array(number + 1).keys()].filter(i=>number % i === 0);
I have done some research on some of the bits but I don't really understand how it goes together as a whole. Thanks in advance :)

Let's break it down:
Array(number + 1): Creates an array with number + 1 empty elements
Array(number + 1).keys(): Retrieves the indices of the array in the form of an iterator
[...Array(number + 1).keys()]: Creates an array from the iterator (contains elements 0, 1, 2, ..., (number - 1), number)
.filter(cb): Calls the function cb for each value in the array, and returns a new array containing the values for which the callback (cb) has returned true (uses implicit boolean conversion)
i => number % i === 0: Gets called for each value of the array, current element stored to i
number % i: Divides number with i and returns the remainder
number % i === 0: Checks if the remainder equal to zero (i.e. if number perfectly divisible by i)
.filter(i => number % i === 0): Filters the array, keeping the elements that are the divisors of number
So,
number => [...Array(number + 1).keys()].filter(i => number % i === 0);
creates a function, that returns an array with the divisors of the given number.

Let's break it down...
Array(number + 1)
This is the array constructor, creating a sparse array with length number + 1
.keys()
Using Array.prototype.keys() to get an iterator for the array keys which are [0, number]
FYI, this is interval notation, not an array
[...Array(number + 1).keys()]
Using the spread syntax on an iterator converts it to an array, so now we have something like
[0, 1, 2, ..., number]
.filter(i=>number % i === 0)
Uses Array.prototype.filter() to reduce the ranged array above to one where the values divide evenly into number
A low-fi version of this might look something like
function factors(number) {
const factors = []
for (let i = 0; i <= number; i++) {
if (i % number === 0) {
factors.push(i)
}
}
return factors
}

Related

Writing a Javascript function to find the average of digits of a number by recursion

I am trying to find the average the sum of digits of a number.
For example, for the number 123, the sum of digits of 123 is 6 and the number of digits in 123 is 3.
So, the average of digits of 123 is 6/3 = 2.
I've only gotten as far as trying to find the sum through recursion unfortunately and often comes up as undefined. If I could figure this out I could find the average comfortably.
function averageOfDigits(number) {
// Make the whole number into a string first to get the individual digits
let arrOfStr = number.toString().split('');
// Convert this array into integers
let arrOfNum = arrOfStr.map(parseFloat)
// Find sum of these digits using recursion
let sum = function sumRecursion (arrOfNum) {
if (arrOfNum.length === 1) {
return arrOfNum[0]
} else {
return arrOfNum.pop() + sum(arrOfNum)
}
}
}
console.log(averageOfDigits(999))
You were close. Your implementation is setting sum equal to the recursive function, so that function is never getting called inside averageOfDigits. I think the confusing part was referring to the same function by two different names.
Here I define the sum function once, then call it twice. First is the internal recursive call, and second is in the return statement.
function averageOfDigits(number) {
// Make the whole number into a string first to get the individual digits
let arrOfStr = number.toString().split('');
// Convert this array into integers
let arrOfNum = arrOfStr.map(parseFloat)
// Find sum of these digits using recursion
function sum(arrOfNum) {
if (arrOfNum.length === 1) {
// base case reached
return arrOfNum[0];
} else {
// return first digit + recursive call
return arrOfNum.pop() + sum(arrOfNum);
}
}
return sum(arrOfNum);
}
console.log(averageOfDigits(999))
You can finish off the averageOfDigits function by replacing the return statement with your own code. Right now it just returns the sum.
It's missing the initial call to the recursive function.
Hint:
return (function sum(arr) {
if (arr.length === 1) {
return arr[0]
} else {
return arr.pop() + sum(arr)
}
}(arrOfNum))
There are several interesting alternative recursive approaches. Our first one treats the number as a string and proceeds from there:
const _avgOfDigits = ([d, ...ds], total, count) =>
d == undefined
? total / count
: _avgOfDigits (ds, total + Number (d), count + 1)
const avgOfDigits = (n) =>
_avgOfDigits (String (n) .split (''), 0, 0)
console .log (avgOfDigits (8675309)) //=> 5.428571428571429
Here we have a shell function that turns our number into an array of single-digit strings, then calls the private recursive function passing that array, and zeros for total and count. Our private function separates off the first digit and adds it to the total, increments the count, and recurs with these values and the remaining digits. When there are no more digits we return the quotient of the total and the digit count.
Our second one is more mathematical:
const avgOfDigits = (n, total = 0, count = 0) =>
n == 0
? total / count
: avgOfDigits (Math .floor (n / 10), total + n % 10, count + 1)
console .log (avgOfDigits (8675309)) //=> 5.428571428571429
Here we deal with the last digit and the remaining ones independently, turning, say, 8675309 into 9 and 867530, and using the 9 to increase our total, again incrementing our count, and recurring with 867530 and these new values. The recursion bottoms out the same way, and we return the same quotient.
A final sample is not recursive, showing an interesting running calculation for our average, not explicitly storing a total anywhere, and deriving the count from the running index:
const avgOfDigits = (n) => String (n) .split ('') .reduce (
(avg, digit, idx) => (avg * idx + Number (digit)) / (idx + 1),
0
)
console .log (avgOfDigits (8675309)) //=> 5.428571428571429
Here we keep a running average, which we adjust using the index as a count of digits seen so far. The efficiency will suffer because on each iteration, we are not just adding two numbers but also performing a multiplication and a division. And it offers little over other simpler versions, but a variant of it could be used successfully with some sort of scan function.
Update
I didn't explain what I meant by that scan comment. The idea is simple enough. scan is a function that acts like reduce but keeps all the partially accumulated values. So scan ((a, b) => a + b)) (0) ([1, 2, 3, 4, 5]) //=> [1, 3, 6, 10, 15], which is [(1), (1 + 2), (1 + 2 + 3), (1 + 2 + 3 + 4), (1 + 2 + 3 + 4 + 5)].
It's easy enough to write a scan function, and with one of those, this style of averaging may become more useful. For example,
const scan = (fn) => (init) => (xs) =>
xs .reduce ((a, x, i) => a .concat (fn (i == 0 ? init : a [i - 1], x, i)), [])
const digitAvgs = scan (
(avg, digit, idx) => (avg * idx + Number (digit)) / (idx + 1),
) (0)
console .log (digitAvgs ([8, 6, 7, 5, 3, 0, 9]))

Why does this recursive partition algorithm return an empty array?

This is part of a much bigger algorithm problem that I am trying to solve.
I am trying to create an array of all numbers less than num.
Here is my Code:
function sum(num, arr = []) {
if (num == 0) {
return arr;
}
arr.push(num);
return sum(num - 1);
}
console.log(sum(10));
I tried declaring arr as a variable and as an argument. I can’t see why it returns an empty array.
How can I fix the array so that it doesn’t return an empty array?
The broader question if you are interested is:
In number theory and combinatorics, a partition of a positive integer n, also called an integer partition, is a way of writing n as a sum of positive integers. Two sums that differ only in the order of their summands are considered the same partition. If order matters, the sum becomes a composition. For example, 4 can be partitioned in five distinct ways:
4
3 + 1
2 + 2
2 + 1 + 1
1 + 1 + 1 + 1
The problem with your first snippet is that you don't pass arr into sum. If you do, you get the array:
function sum(num, arr = []) {
if (num == 0) {
return arr;
}
arr.push(num);
return sum(num - 1, arr);
// ^^^−−−−−−−−−−−−−−−−−−−−−−−−
}
console.log(sum(10));
It returns empty array because you did not send in the second parameter, which is arr, hence each recursion is initialized with an empty array.
function sum(num, arr = []) {
if (num == 0) {
return arr;
}
arr.push(num);
return sum(num - 1, arr);
}
console.log(sum(10));

Problem with JS Math methods: function is removing multiple min/max values (I think?)

I'm doing a coding challenge and I stumbled upon an issue that I think lies in the Math.min and Math.max methods. The goal of the function is to return the sum of an array after removing the min and max values (but only one of each e.g. [1,1,2,3,3] should only remove a single 1 and 3). Other rules are returning 0 for empty arrays, single value arrays, and null - this part seems to be working fine.
I ran some tests and I noticed my function was filtering multiple min and max values. For instance, my function returns 16 instead of 17 for sumArray([ 6, 2, 1, 1, 8, 10 ])
const sumArray = array => {
const minMax = num => {
return num !== Math.max(...array) && num !== Math.min(...array);
};
const reducer = (total, num) => {
return total + num;
};
// len check to prevent empty arrays after min/max removal
if (array === null || array.length <= 2) {
return 0
}
return array.filter(minMax).reduce(reducer);
}
console.log(sumArray([6, 2, 1, 1, 8, 10]));
I'm learning JS, so maybe I'm not even onto the real problem here. I just know the function works in all test cases except those that have multiple min or max values. Hoping to learn and improve from this, as it's my first time playing around with arrays. Thanks.
I really wouldn't botter to do any of the filtering, just remove once the min value and once the maximum value after you summed the array :)
const sumArray = array => {
const reducer = (total, num) => {
return total + num;
};
// len check to prevent empty arrays after min/max removal
if (array === null || array.length <= 2) {
return 0
}
const min = Math.min( ...array );
const max = Math.max( ...array );
return array.reduce(reducer) - min - max;
}
console.log(sumArray([6, 2, 1, 1, 8, 10]));
Your original code doesn't work (and is actually "expensive" because you recalculate the minimum and maximum on every iteration), because you will remove all minimum values and all maximum values, and not only once, as you explained the rules were.
Your minMax filter is filtering out all occurrences of the highest or lowest values.
All values that match the lowest or highest value are removed there. That's not a bug in min or max, but in how you use the returned value.
I'd suggest reworking your function:
Find the min / max, and store those in variables.
Add up everything in the array, store that in a variable.
Subtract the min and max from the total.

Calculating binary of array using array.reduce

I have tried to calculate binary of a number provided in an array using naive for-loop method.
Than I find a solution which works but I am not able to understand how it is working.
I need help to understand this.
const binaryArrayToNumber = arr => {
return arr.reduce((a,b)=>(a<<1|b),0);
};
console.log(binaryArrayToNumber([1,1,1,1]))
Explaining your code:
The << left logical shift) shifts the number to the left. If the number n is 00010110 then:
n << 1 will be 00101100.
n << 2 will be 01011000.
etc
The operator | (betwise or) performs an iclusive logical OR on the bits at the same positions. If the numbers n and m were, respectively 00110100 and 10010101 then:
n = 00110100
m = 10010101
----------------
n | m = 10110101
following these rules: 0 | 0 = 0, 0 | 1 = 1, 1 | 0 = 1 and 1 | 1 = 1.
Now for the reduce part: reduce loop through an array, passing an accumulator (initially set by the user, the last parameter) and the current item of the array to a callback (the function passed as the first parameter) and set the value of the accumulator to the returned value of that callback.
So for each item in the array we shifts the accumulator to the left to make a place for the new bit and then add the bit to that place using the betwise or. Here is an example with explanation:
var arr = [1, 0, 1];
reduce will start with an accumulator equal to 0 (...,0); at end of reduce line) then pass it along with the current item (the first item) to the callback. The the callback will shift the accumulataror (a) to the left by 1:
First shift the accumulator:
a = 00000000;
a << 1 = 00000000;
And then return the betwise or result of the shifted a with the current item from the array b:
b = 00000001;
00000000 | b = 00000001;
Now the new accumulator is the result of the above code (00000001).
reduce then will pass the current accumulator along with the current item from the array (now the second one) to the callback again:
First:
a = 00000001;
a << 1 = 00000010;
and:
b = 00000000;
00000010 | b = 00000010;
reduce will do the same thing for the last item of the array:
First:
a = 00000010;
a << 1 = 00000100;
and:
b = 00000001;
00000100 | 00000001 = 00000101;
Since there is no more items in the array, reduce will return the accumulated value (the accumulator return by the last call to the callback i.e a) as the result.
If the syntax return arr.reduce((a,b)=>(a<<1|b),0); isn't clear for you, it's because you're not familliar with Arrow functions. The exact line could be written using regular functions like this:
return arr.reduce(function(a, b) { // a the accumulator, b the curent item from the array
return (a << 1) | b; // return something to store as the new accumulator
}, 0; // 0 is the initial value of the accumulator
Another way to do it:
without using any binary operation nor reduce:
var arr = [1, 0, 1, 1];
var number = parseInt(arr.join(''), 2);
console.log(number);
arr.join('') will return a string (a concatenation of all the items in the array "1011"). Then parseInt will parse that string as being a binary number.
The reduce function works as an accumulator. The (a,b) pair is actually (total, currentValue). For instance, if you want to calculate sum of 1, 2 and 3, you can use the following code for that:
var sum = [1, 2, 3].reduce(
function(total, currentNumber){ return total + currentNumber; }
, 0);
For each iteration, a value of the total variable is increased for currentNumber and after all elements of the array were iterated, the total is being assigned to the sum variable.
The second parameter of the anonymous function (in this case 0) is the initial value of the sum (before iterating the array elements).
So, the code above is same as this code:
var sum = 0;
for (var i = 1; i <=3; i++)
{
sum = sum + i;
}

Array Addition I JavaScript function on Coderbyte

I am doing a challenge on Coderbyte and I would be grateful for any advice on my question:
The challenge given to me:
"Using the JavaScript language, have the function ArrayAdditionI(arr)
take the array of numbers stored in arr and return the string true if
any combination of numbers in the array can be added up to equal the
largest number in the array, otherwise return the string false.
For example: if arr contains [4, 6, 23, 10, 1, 3] the output should
return true because 4 + 6 + 10 + 3 = 23. The array will not be empty,
will not contain all the same elements, and may contain negative numbers. "
The way I attempted to solve it: http://jsfiddle.net/reLsg0fg/
function ArrayAdditionI(arr){
var newArr=arr.sort(); // sorted from smallest to largest.
var largestNum=newArr.slice(-1); // Gets the last number, which would be the largest.
var loopArr=arr.sort().pop(); // Takes out the largest number for adding later.
var result=0;
for(var i=0; i<loopArr.length; i++){ // loops through all numbers.
if(result/largestNum !== 1){ //when you divide a number by itself it will be 1.
result+=loopArr[i]; // keep adding each number until get largest number.
}else if(result === largestNum){
return true;
}
}
return false;
}
// TESTS
console.log("-----");
console.log(ArrayAdditionI([4,6,23,10,1,3]));
console.log(ArrayAdditionI([5,7,16,1,2]));
console.log(ArrayAdditionI([3,5,-1,8,12]));
I'm supposed to get true, false, true. But I get false, false, false as if something is wrong within my loop. JSFiddle: http://jsfiddle.net/reLsg0fg/
I would appreciate any suggestions. Thank you ^^
Sort Array using
arr.sort(function (a, b) { return a - b })
I have tried to solve this problem with a for loop but I missed the fact that the challenge
is not asking that all numbers need to add up to equal the largest num, but it is also possible to
add up to the largest num if we take some numbers out. Thus I decided to solve with recursion.
Tips:
* The Math.max.apply() method takes an array and returns the largest number. Note that it usually works on strings as Math.max().
* the sort() method can take a parameter to further expand it's purpose. Usually it only
sorts strings, but to sort numbers we include a function that finds which number is bigger.
* First get the largest number.
* Sort the array and remove the largest number to be used for recursion later.
* Create a recursion function that checks if the numbers add up to the largest number, and if not, check that if some numbers in array are subtracted from the largest num they are equal to the largest number.
function ArrayAdditionI(array){
var largestNum = Math.max.apply(0, array); // gets the largest number in array.
array.sort(function(a,b){ return a-b;}).pop(); // sorts array and removes last(largest) number.
function recursionCheck(arr, sum){
// the base case when array empty.
if(arr.length === 0){
return sum === 0;
}
var arrBeginNum=arr[0];
// for every recursion take away one number(the first one in this case).
arr = arr.slice(1);
// first check if numbers sum up to largest num if not, check if removing numbers adds up to largest num.
return recursionCheck(arr, sum) || recursionCheck(arr, sum - arrBeginNum);
}
// recursion needs to be called for it to start.
return recursionCheck(array, largestNum);
}
// TESTS
console.log("-----");
console.log(ArrayAdditionI([1,2,3,5,4])); ==> true
console.log(ArrayAdditionI([21,10,12,9,2])); ==> true
console.log(ArrayAdditionI([4,6,23,10,1,3])); ===> true
console.log(ArrayAdditionI([5,7,16,1,2])); ===> false
console.log(ArrayAdditionI([3,5,-1,8,12])); ===> true
This might not be the complete solution yet, but here are the JavaScript-Problems:
largestNum was an array in you algorithm
.sort() was not working
function ArrayAdditionI(arr){
var largestNum = Math.max.apply(0, arr); // Gets the last number, which would be the largest.
arr.sort(function(a, b){return a-b})
arr.pop(); // Takes out the largest number for adding later.
var result=0;
Also use if(result !== largestNum) {, Division is expensive and might have unexpected results with floating-point numbers.
Thats it for your JavaScript. But I am pretty sure the Algorithm is wrong - but I think this is up to you
Note that the example [4, 6, 23, 10, 1, 3] => 4 + 6 + 10 + 3 = 23 is not just adding up the lowest to the biggest value to try and match it.
A possible example of a solution for the problem.
How this works:
First sort all items descending
Shift the first element to largest
Call the recursive function y with the reduced array, the largest value and a variable which holds an empty array for the successfully added items.
The recursive function works basically in two parts
Test if the remaining sum is zero, if so the result is achieved and return true, which finished the function.
If not iterate through the array and
Make a copy from the array
Get the value from the position with splice
Test, if the value is smaller or equal the remaining sum and the result of the call of y with the shorted array, sum minus value and a new array with the used items and the acual item.
If true return true and finish the function.
If not finished before return false.
function x(array) {
function y(a, s, result) {
var aa, i, v;
if (s === 0) {
document.write('<pre>result: ' + JSON.stringify(result, 0, 4) + '</pre>');
return true;
}
for (i = 0; i < a.length; i++) {
aa = a.slice();
v = aa.splice(i, 1)[0];
if (v <= s && y(aa, s - v, result.concat(v))) {
return true;
}
}
return false;
}
var largest,
r = [];
array.sort(function (a, b) { return b - a; });
largest = array.shift();
document.write('largest value: ' + largest + '<br>');
return y(array, largest, r);
}
document.write(x([4, 6, 23, 10, 1, 3]) + '<hr>');
document.write(x([5, 7, 16, 1, 2]) + '<hr>');
document.write(x([3, 5, -1, 8, 12]));
Thanks #mar
Here is a version in Kotlin if someone needs
private fun returnResult(arr: Array<Int>): Boolean {
arr.sort()
val largestNumber = arr.last()
val arrWithoutLargest = arr.dropLast(1).toTypedArray()
return recursionCheck(arrWithoutLargest, largestNumber)
}
private fun recursionCheck(arr: Array<Int>, sum:Int): Boolean {
if (arr.isEmpty()) return sum == 0
val arrBeginNum = arr[0]
val arr2 = arr.drop(1).toTypedArray()
return recursionCheck(arr2, sum) || recursionCheck(arr2, sum - arrBeginNum)
}

Categories