let items = ['Tom','Bill','Kim'];
let result = items.reduce((str, item) => str + '<'.concat(item).concat('>'),"");
console.log(result);
May I ask what the ending "" does to the code?
It's not related to concat, it's related to reduce. It's the second argument to reduce, which sets the initial value of the accumulator. E.g., it "seeds" the accumulation, so it's often called the "seed value" or just "seed."
Without that argument, the first call to the callback would get the entries at indexes 0 and 1 as its first two arguments. With that argument, the first callback receives "" as its first argument and the entry at index 0 as its second argument.
Side note:
It's probably worth noting that that use of concat probably isn't the best way to do what that callback is trying to do. The author of that code is mixing string concatenation via + with concat, which is a bit odd. I'd use one or the other, or a template literal:
Using +:
let items = ['Tom','Bill','Kim'];
let result = items.reduce((str, item) => str + '<' + item + '>', "");
console.log(result);
Using concat:
let items = ['Tom','Bill','Kim'];
let result = items.reduce((str, item) => str.concat('<', item, '>'), "");
console.log(result);
Using a template literal:
let items = ['Tom','Bill','Kim'];
let result = items.reduce((str, item) => `${str}<${item}>`, "");
console.log(result);
It's the second argument to reduce as the initial value for accumulator.
Understand this way - With a single example
// Add all numbers in arr
const numbers = [1, 2, 3, 4]
let sum = 0 // this is the initial value
for (let n of numbers)
sum += n
console.log(sum)
// Again add all numbers in arr using reduce
const numbers = [1, 2, 3, 4]
let sum = 0
sum = numbers.reduce((accumulator, currentValue) => {
return accumulator + currentValue
}, sum)
/**
* First round - accu = 0, currVal = 1 => accu = 1
* Second round - accu = 1, currVal = 2 => accu = 3
* Third round - accu = 3, currVal = 3 => accu = 6
* Fourth round - accu = 6, currVal = 4 => accu = 10
* Returns accu = 10
*/
console.log(sum)
// Reduce takes a callback
// Accumulator will get the initial value using sum
// currentValue is the each iteration value
Internally reduce returns accumulator + currentValue and save it to accumulator each iterate.
Related
I am trying to get better at understanding recursion so that I can get better at implementing dynamic programming principles. I am aware this problem can be solved using Kadane's algorithm; however, I would like to solve it using recursion.
Problem statement:
Given an array of integers, find the subset of non-adjacent elements with the maximum sum. Calculate the sum of that subset.
I have written the following partial solution:
const maxSubsetSum = (arr) => {
let max = -Infinity
const helper = (arr, len) => {
if (len < 0) return max
let pointer = len
let sum = 0
while (pointer >= 0) {
sum += arr[pointer]
pointer -= 2
}
return max = Math.max(sum, helper(arr, len - 1))
}
return helper(arr, arr.length - 1)
}
If I had this data:
console.log(maxSubsetSum([3, 5, -7, 8, 10])) //15
//Our subsets are [3,-7,10], [3,8], [3,10], [5,8], [5,10] and [-7,10].
My algorithm calculates 13. I know it's because when I start my algorithm my (n - 2) values are calculated, but I am not accounting for other subsets that are (n-3) or more that still validate the problem statement's condition. I can't figure out the logic to account for the other values, please guide me on how I can accomplish that.
The code is combining recursion (the call to helper inside helper) with iteration (the while loop inside helper). You should only be using recursion.
For each element of the array, there are two choices:
Skip the current element. In this case, the sum is not changed, and the next element can be used. So the recursive call is
sum1 = helper(arr, len - 1, sum)
Use the current element. In this case, the current element is added to the sum, and the next element must be skipped. So the recursive call is
sum2 = helper(arr, len - 2, sum + arr[len])
So the code looks like something this:
const maxSubsetSum = (arr) => {
const helper = (arr, len, sum) => {
if (len < 0) return sum
let sum1 = helper(arr, len - 1, sum)
let sum2 = helper(arr, len - 2, sum + arr[len])
return Math.max(sum1, sum2)
}
return helper(arr, arr.length - 1, 0)
}
Your thinking is right in that you need to recurse from (n-2) once you start with a current index. But you don't seem to understand that you don't need to run through your array to get sum and then recurse.
So the right way is to
either include the current item and recurse on the remaining n-2 items or
not include the current item and recurse on the remaining n-1 items
Lets look at those two choices:
Choice 1:
You chose to include the item at the current index. Then you recurse on the remaining n-2 items. So your maximum could be item itself without adding to any of remaining n-2 items or add to some items from n-2 items.
So Math.max( arr[idx], arr[idx] + recurse(idx-2)) is the maximum for this choice. If recurse(idx-2) gives you -Infinity, you just consider the item at the current index.
Choice 2:
You didn't choose to include the item at the current index. So just recurse on the remaining n-1 items - recurse(n-1)
The final maximum is maximum from those two choices.
Code is :
const maxSubsetSum = (arr) => {
let min = -Infinity
const helper = (arr, idx) => {
if ( idx < 0 ) return min
let inc = helper(arr, idx-2)
let notInc = helper(arr, idx-1)
inc = inc == min ? arr[idx] : Math.max(arr[idx], arr[idx] + inc)
return Math.max( inc, notInc )
}
return helper(arr, arr.length - 1)
}
console.log(maxSubsetSum([-3, -5, -7, -8, 10]))
console.log(maxSubsetSum([-3, -5, -7, -8, -10]))
console.log(maxSubsetSum([-3, 5, 7, -8, 10]))
console.log(maxSubsetSum([3, 5, 7, 8, 10]))
Output :
10
-3
17
20
For the case where all items are negative:
In this case you can say that there are no items to combine together to get a maximum sum. If that is the requirement the result should be zero. In that case just return 0 by having 0 as the default result. Code in that case is :
const maxSubsetSum = (arr) => {
const helper = (arr, idx) => {
if ( idx < 0 ) return 0
let inc = arr[idx] + helper(arr, idx-2)
let notInc = helper(arr, idx-1)
return Math.max( inc, notInc )
}
return helper(arr, arr.length - 1)
}
With memoization:
You could memoize this solution for the indices you visited during recursion. There is only one state i.e. the index so your memo is one dimensional. Code with memo is :
const maxSubsetSum = (arr) => {
let min = -Infinity
let memo = new Array(arr.length).fill(min)
const helper = (arr, idx) => {
if ( idx < 0 ) return min
if ( memo[idx] !== min) return memo[idx]
let inc = helper(arr, idx-2)
let notInc = helper(arr, idx-1)
inc = inc == min ? arr[idx] : Math.max(arr[idx], arr[idx] + inc)
memo[idx] = Math.max( inc, notInc )
return memo[idx]
}
return helper(arr, arr.length - 1)
}
A basic version is simple enough with the obvious recursion. We either include the current value in our sum or we don't. If we do, we need to skip the next value, and then recur on the remaining values. If we don't then we need to recur on all the values after the current one. We choose the larger of these two results. That translates almost directly to code:
const maxSubsetSum = ([n, ...ns]) =>
n == undefined // empty array
? 0
: Math .max (n + maxSubsetSum (ns .slice (1)), maxSubsetSum (ns))
Update
That was missing a case, where our highest sum is just the number itself. That's fixed here (and in the snippets below)
const maxSubsetSum = ([n, ...ns]) =>
n == undefined // empty array
? 0
: Math .max (n, n + maxSubsetSum (ns .slice (1)), maxSubsetSum (ns))
console.log (maxSubsetSum ([3, 5, -7, 8, 10])) //15
But, as you note in your comments, we really might want to memoize this for performance reasons. There are several ways we could choose to do this. One option would be to turn the array we're testing in one invocation of our function into something we can use as a key in an Object or a Map. It might look like this:
const maxSubsetSum = (ns) => {
const memo = {}
const mss = ([n, ...ns]) => {
const key = `${n},${ns.join(',')}`
return n == undefined
? 0
: key in memo
? memo [key]
: memo [key] = Math .max (n, n + maxSubsetSum (ns .slice (1)), maxSubsetSum (ns))
}
return mss(ns)
}
console.log (maxSubsetSum ([3, 5, -7, 8, 10])) //15
We could also do this with a helper function that acted on the index and memoized using the index for a key. It would be about the same level of complexity.
This is a bit ugly, however, and perhaps we can do better.
There's one issue with this sort of memoization: it only lasts for the current run. It I'm going to memoize a function, I would rather it holds that cache for any calls for the same data. That means memoization in the definition of the function. I usually do this with a reusable external memoize helper, something like this:
const memoize = (keyGen) => (fn) => {
const cache = {}
return (...args) => {
const key = keyGen (...args)
return cache[key] || (cache[key] = fn (...args))
}
}
const maxSubsetSum = memoize (ns => ns .join (',')) (([n, ...ns]) =>
n == undefined
? 0
: Math .max (n, n + maxSubsetSum (ns .slice (1)), maxSubsetSum (ns)))
console.log (maxSubsetSum ([3, 5, -7, 8, 10])) //15
memoize takes a function that uses your arguments to generate a String key, and returns a function that accepts your function and returns a memoized version of it. It runs by calling the key generation on your input, checks whether that key is in the cache. If it is, we simply return it. If not, we call your function, store the result under that key and return it.
For this version, the key generated is simply the string created by joining the array values with ','. There are probably other equally-good options.
Note that we cannot do
const recursiveFunction = (...args) => /* some recursive body */
const memomizedFunction = memoize (someKeyGen) (recursiveFunction)
because the recursive calls in memoizedFunction would then be to the non-memoized recursiveFunction. Instead, we always have to use it like this:
const memomizedFunction = memoize (someKeyGen) ((...args) => /* some recursive body */)
But that's a small price to pay for the convenience of being able to simply wrap up the function definition with a key-generator to memoize a function.
This code was accepted:
function maxSubsetSum(A) {
return A.reduce((_, x, i) =>
A[i] = Math.max(A[i], A[i-1] | 0, A[i] + (A[i-2] | 0)));
}
But trying to recurse that far, (I tried submitting Scott Sauyet's last memoised example), I believe results in run-time errors since we potentially pass the recursion limit.
For fun, here's bottom-up that gets filled top-down :)
function f(A, i=0){
if (i > A.length - 3)
return A[i] = Math.max(A[i] | 0, A[i+1] | 0);
// Fill the table
f(A, i + 1);
return A[i] = Math.max(A[i], A[i] + A[i+2], A[i+1]);
}
var As = [
[3, 7, 4, 6, 5], // 13
[2, 1, 5, 8, 4], // 11
[3, 5, -7, 8, 10] // 15
];
for (let A of As){
console.log('' + A);
console.log(f(A));
}
I need to calculate the average value of my datas, my datas are in an
array the problem is when i parseInt them into an integer i only get
one value back and when try as an example following thing:
console.log(array[0]/24)
i dont get nothing
this is my array:
info my array is coming from php to js (php connected to api)
var signal_GW1 = dataFromAjax.data.data1.map(function(innerData){
return innerData.map(function(row){
return row[1];
});
});
console.log(signal_GW1);
//OUTPUT on BROWSER CONSOLE
[Array(136)]
0: Array(136)
[0 … 99]
0: "-59"
1: "-59"
2: "-59"
3: "-59"
4: "-53"
5: "-63"
To get the average of your array you could do the following.
As the response from the server is returning strings you need to use parseInt to convert to a number.
const array = ['-59', '-57', '-59', '-57'];
const average = array.reduce((accumulator, currentValue) => parseInt(accumulator) + parseInt(currentValue)) / array.length;
console.log(average);
const myArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let sum = 0
for (const value of myArray) { sum += value; }
const average = sum / myArray.length
console.log(`Average: ${average}`)
Basically: calculate the sum using Array.prototype.reduce, and divide the sum by the number of Array values (i.e. its length). About Array.prototype.reduce
const x = Array.apply(null, new Array(5))
.map(() => String(-Math.floor(100 * Math.random() + 1)));
// (+v) converts the string value to a Number
const meanX = x.reduce((a, v) => a + (+v), 0) / x.length;
console.log(`${JSON.stringify(x)} => Mean: ${meanX}`);
// or as a one liner
console.log(`Mean from one liner: ${
x.reduce( (a, v) => a + ((1/x.length) * +(v)), 0 ).toFixed(2)}`
);
After reading the docs on map method,I still cannot get this to work.
I am trying to use map to get the average of every pair of numbers in an array.Please help me understand whats wrong.
function getAverage(num1,num2){return Math.ceil((num1+num2)/2)};
function a(input){ var b = input.map(getAverage(num1,num2)); return b; }
a([1,2,3,4]) //error num1 is not defined
//expected [2,4]
map projects a function to each element of a list/array, it simply "maps" a function over all the items.
[1, 2, 3].map(function (number) { return number + 1; });
// -> [2, 3, 4]
Therefor, first you need to have pairs of items in your "input" array, so it looks like this:
var numberPairs = [[1, 2], [3, 4]]
Until now, all you have are just single numbers but no pairs.
After conversion, you can use map like this:
numberPairs.map(function (pair) {
return Math.ceil((pair[0] + pair[1]) / 2);
});
This will give:
[2, 4]
as a result.
You can't calculate the average using a map. A mapping pass a function to each element and then returns an array with the same shape. That is not the case, you want to get a value from an array, and you can use the reduce method for that.
// adds two number
const adder = (a,b) => a + b;
// reduces the array adding all numbers and divides
// by the array length
const getAverage = (arr) => arr.reduce(adder)/arr.length;
// outputs 2.5
console.log(getAverage([1,2,3,4]))
You can use reduce() instead of map() to aggregate averages of every n values in the array:
const sum = array => array.reduce((a, b) => a + b, 0)
const getAverage = n => (averages, value, index, array) => index % n === 0
? [...averages, Math.ceil(sum(array.slice(index, index + n)) / n)]
: averages
const result = [1, 2, 3, 4].reduce(getAverage(2), [])
console.log(result)
So I need to solve this problem STRICTLY using recursion
// 2. Compute the sum of an array of integers.
// sum([1,2,3,4,5,6]); // 21
And then I'm testing this solution in PythonLive
var sum = function(array) {
if(array.length===0){
return array
}
return array.slice(0,array.length)+sum(array.pop())
};
sum([1,2,3,4,5,6]);
Then at step 6 it says "TypeError: array.slice is not a function"
I don't understand why if it already worked taking 6 off the array and returning the remaining array...
Can someone explain me what am I doing wrong please?
thanks! :)
If you look at the return values you will see that you are always returning an array. This can't be right when you want a number as a final result. When array.length === 0 you can safely return 0 because that's the same of an empty array. That's your edge condition. After that you just want the sum of one element plus the rest.
You can also just return the array length when it's zero making for a very succinct solution. && shortcircuits returning the left element if it's false (like 0) otherwise the second:
var sum = (array) => array.length && array.pop() + sum(array)
console.log(sum([1,2,3,4,5,6]));
If you prefer slice you could also this, which is basically the same:
var sum = (array) => array.length && array[0] + sum(array.slice(1))
console.log(sum([1, 2, 3, 4, 5, 6]));
Recursive sum function:
const sum = list => {
if (!list.length) return 0;
return list.pop() + sum(list);
};
Because .pop mutates the array, you don't need to use slice. For a non-destructive version (doesn't alter the original array):
const sum = ([first, ...rest]) => {
if (first === undefined) return 0;
return first + sum(rest);
};
The issue with your code is that you are processing the values the wrong way around, it should be
return sum(array.slice(0,array.length-1)) + array.pop();
In fact since array.pop() removes the element, you can just do it this way around:
return array.pop() + sum(array);
You also need to return 0 when array.length===0, otherwise the sum will fail.
if (array.length===0) return 0;
However it's much simpler just do this with reduce:
let arr = [1,2,3,4,5,6];
console.log(arr.reduce((t, v) => { return t + v; }, 0));
Another encoding using an explicit empty and pure expressions
const empty = x =>
x === empty
const sum = ([ x = empty, ...rest ]) =>
empty (x)
? 0
: x + sum (rest)
console.log
( sum ([ 1, 2, 3, 4, 5 ]) // 15
, sum ([]) // 0
)
Your other question that asks how to sum a nested array was put on-hold for reasons I don't understand. You can adapt the above implementation to support input of nested arrays
const empty = x =>
x === empty
const sum = ([ x = empty, ...rest ]) =>
empty (x)
? 0
: Array.isArray (x)
? sum (x) + sum (rest)
: x + sum (rest)
console.log
( sum ([ 1, [ 2, [ 3, 4 ], 5 ]]) // 15
, sum ([ 1, 2, 3, 4, 5 ]) // 15
, sum ([[[]]]) // 0
, sum ([]) // 0
)
I have array of elements as input.all I need is to double the value of array elements.Though it is simple to make use of map to get the solution.I am interested in using reduce.eg:
io: var a = [1,2,3,4,5];
op:[2,4,6,8,10]; .but this is where I ended.
var c = a.reduce( (acc,b) => {
acc =b*2;
console.log(acc);
return acc
},[]);
var arr = [1, 2, 3, 4, 5];
var doubled = arr.reduce(function (memo, val) {
memo.push(val * 2);
return memo;
}, []);
console.log(doubled);
var arr2 = [1, 2, 3, 4, 5];
arr2.reduce(function (memo, val, i) {
memo[i] *= 2;
return memo;
}, arr2);
console.log(arr2);
var arr3 = [1, 2, 3, 4, 5];
arr3.reduce(function (memo, val, i) {
arr3[i] *= 2;
}, null);
console.log(arr3);
In the first solution, reduce starts with an empty array (the second argument provided to it). Then, that array is passed down to next iterations as the first argument (memo) to the function we provided. The second argument is the current element of the iteration. After the doubled value is pushed in the new array, that array is returned so it can be accessed in future iterations as memo.
In the second solution, no new array is created and the initial one is used instead. It is passed to reduce as its second element and later accessed through memo.
The third solution is like the second one except the reduced array is just referenced as it is. Notice that null must be passed as second argument. If nothing is passed, reduce will start from the second element (since there's no initial value) and the first element won't get doubled.
Note
The first time the callback is called, accumulator and currentValue can be one of two values. If no initialValue is provided, then accumulator will be equal to the first value in the array, and currentValue will be equal to the second.
let arrayOfNumbers = [1, 2, 3, 4];
arrayOfNumbers.reduce((accumulator, currentValue, index, array) => {
//code
},initialvalue);
if you miss this initialvalue argument then your index begins from one(1) and the accumulator will have the first value of array.
Hence your array[index] = array[index]*2
For example in such case
arr=[1,2,3,4,5]
output
arr=[1,4,6,8,10]
Now this will be the approach
let arrayOfNumbers = [1, 2, 3, 4];
arrayOfNumbers.reduce((accumulator, currentValue, index, array) => {
console.log(accumulator,currentValue);
array[index] = array[index]*2;
},0);
arr=[1,2,3,4,5]
output
arr=[1,4,6,8,10]
take initialvalue as 0(or any value), so that you can get the index from 0.
if you do this accumulator will get 0 and the index begins from 0 only.
Note
if you don't initialize accumulator value that is initialvalue.
Default
accumulator(initialvalue) will be arr[0]
and index begins from 1
and
if you initialize accumulator value that is initialvalue(argument) then
accumulator(initialvalue) will take that initialvalue and index begins from 0.
As you provided in your last parameter to reduce, acc is an array, although you're treating it like an integer. You'll want something like below:
var c = a.reduce( (acc,b) => {
acc.push(b*2);
console.log(acc);
return acc
},[]);
Try this
var c = a.reduce( (acc,b) => acc.concat(b*2)
,[]);
I'll provide a couple other answers that aren't shown here
1. Using spread syntax
const data =
[1,2,3,4,5];
const double = xs =>
xs.reduce ((ys, x) => [...ys, x * 2], [])
console.log(double(data))
// [ 2, 4, 6, 8, 10 ]
2. Using a destructured parameter with recursion
const data =
[1,2,3,4,5]
// this time we don't need reduce
const double = ([x,...xs]) =>
x === undefined ? [] : [x * 2, ...double(xs)]
console.log(double(data))
// [ 2, 4, 6, 8, 10 ]
3. Tacit (Point-free style)
const data =
[1,2,3,4,5]
const mult = x => y =>
y * x
const reduce = f => acc => ([x,...xs]) =>
x === undefined
? acc
: reduce (f) (f (acc, x)) (xs)
const map = f =>
reduce ((acc, x) =>
[...acc, f (x)]) ([])
// point-free double
const double =
map (mult (2))
console.log (double (data))
// [ 2, 4, 6, 8, 10 ]