I am new to Javascript and was learning the reduce function today and going through the examples in Mozzila Developer and I slightly modified one and it gave me a result which I can not comprehend (since I am very new). Can someone pls explain the result here and the "rules" behind it? Thanks
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(accumulator, currentValue) {
return accumulator + currentValue
});
console.log(flattened)
Answer: 0, 12, 34, 5
Because you didn't pass an initial value as the accumulator, the initial value here is the array [0, 1]. On each iteration, the operation accumulator + currentValue is performed and used as the new accumulator on the next iteration (or, as the entire return value of the .reduce, if on the last iteration).
On the first iteration, [0, 1] + [2, 3] results in "0,12,3". This is because when non-numbers are used with +, they are converted into primitives first, and when arrays are converted to primitives, each item is joined by a comma. So
[0, 1] + [2, 3]
// evaluates to
'0,1' + '2,3'
// evaluates to
'0,12,3'
On the next iteration:
'0,12,3' + [4, 5]
// evaluates to
'0,12,3' + '4,5'
// evaluates to
'0,12,34,5'
That's the last iteration, so the final result is '0,12,34,5'.
Note that there are no spaces - the array items are joined only by a comma, not by a comma and a space.
It because reduces method reduces the array to a single value.
In your case, you are adding the arrays([0,1], [2,3], [4,5]) which return a string
Hence, the accumulator is accumulating the sum of iterated currentValue.
Working:
the loop works in the following steps
accumulator = "0,1"
accumulator = "0,1" + "2,3"
accumulator = "0,1" + "2,3" + "4,5"
readable output = "0,1 2,3 4,5"
System Generated Output = "0,12,34,5"
Related
I'm trying to solve a coding challenge
it gives me an array and waits for the answer ,then gives me the next array and so on.
https://www.codewars.com/kata/5648b12ce68d9daa6b000099/train/javascript
I am trying to take this input: var busStops = [[10,0],[3,5],[5,8]] and return 5.
the code is supposed to add the first in each pair to a total while subtracting the second in each pair from the total eg: 10 - 0 + 3 - 5 + 5 - 8 = 5
First my code loops through the inner arrays and outer array ,pushing it into myarr as a regular array eg: [10,0,3,5,5,8].
It then adds the value if it is index is 0 or even and subtracts it if the index is odd.
This actually works!
Until it is given a second array eg: [[3,0],[9,1],[4,10],[12,2],[6,1],[7,10]]
It is still calculating the total correctly but is still remembering the total from the first array meaning it is returning 22 instead of 17
Why?
There is a var answer = 0 that is being executed ahead of the second loop
It should forget the value of the previous answer.
Right?
Edit: I figured out my problem. I just needed to empty myarr after the total was calculated!
let myarr = [];
var number = function (busStops) {
for (var i = 0; i < busStops.length; i++) {
for (var j = 0; j < busStops[i].length; j++) {
/*console.log(busStops[i][j]);*/
myarr.push(busStops[i][j]);
}
}
console.log("test");
var answer = 0;
console.log("test again");
for (let t = 0; t < myarr.length; t++) {
if (t == 0 || t % 2 == 0) {
answer = answer + myarr[t];
} else {
answer = answer - myarr[t];
}
}
console.log(answer);
return answer;
};
The task at your hand tries to somehow find a value (an integer) from an array of arrays (multidimensional array). That task seems to be reducing that multidimensional array into a single integer.
Luckily, JavaScript has some powerful array methods and one of them is the reduce method:
The reduce() method executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value. Source: MDN
The reduce method tries to reduce an array's elements into a single value and in your case we want to reduce that multidimensional array into a single value that is the number persons who are still in the bus.
Before typing some code, let's dig a bit deeper into the reduce method:
It accepts 2 parameters, a callback function that acts as the reducer and the initial value to be used in the first iteration of the reduce method.
The reducer callback function, on its own, accepts 4 parameters that are supplied by the reduce method. You may learn more about those parameters here as am only going to focus on the first 2 parameters the reducer accepts:
previousValue: hold the value from the previous reducer call. On first call, it contains the value you set to the initial value parameter of the reduce method or, if you didn't supply an initial value, previousValue shall hold the value of your array's first element (arr[0]).
currentValue: hold the current reduce iteration's item.
Now, let's get back to the task we have, we need to calculate the number of persons who are still in the bus based on a supplied multidimensional array. Each item in that multidimensional array is an array of two values where the result we need at the end is: the sum of the differences between each array, in the multidimensional array, first and second values (sum = multiDim[i][0] - multiDim[i][1] + multiDim[i + 1][0] + multiDim[i + 1][1] etc...).
To solve this task, we'll reduce that multidimensional array into a single number (let's call it result) by using a simple reducer function that will start by an initial value of 0 (as we're calculating a sum in our case) and will add, to the result, the difference between the first and the second values of the array supplied by the reduce at each iteration.
To illustrate, here's a live demo:
/**
* a function that calculates and returns the number of person who are still in the bus or precisely, the sum of the differences between "c[0]" and "c[1]"
* busArray: the supplied multidimensional array to reduce
* the reducer accepts two parameters:
* r: the result from the last call to the reducer function (initially set to 0, the initial value (see second parameter passed to the "reduce" method))
c: hold the current iteration's array.
*/
const calculateWhoAreInTheBus = busArray => busArray.reduce((r, c) => r += c[0] - c[1], 0);
/** test the created "calculateWhoAreInTheBus" function */
console.log(calculateWhoAreInTheBus([
[10, 0],
[3, 5],
[5, 8]
])); // should print: 5
console.log(calculateWhoAreInTheBus([
[3, 0],
[9, 1],
[4, 10],
[12, 2],
[6, 1],
[7, 10]
])); // should print: 17
console.log(calculateWhoAreInTheBus([
[3, 0],
[9, 1],
[4, 8],
[12, 2],
[6, 1],
[7, 8]
])); // should print: 21
console.log(calculateWhoAreInTheBus([
[0, 0],
[0, 0]
])); // should print: 0
I would advice you to use Array.prototype.reduce instead. For example like this:
const reducer = (previous, current) => previous + current[0] - current[1];
const answer = busStops.reduce(reducer, 0);
It is very brief (although this is not a goal in and of itself) and the reducer function does almost trivial work, so it does not complicate unneccesarily. Best of all it encapsulates the functionality with a minimal need of extra variables.
Othwerwise you could simplify your function a bit but use the let keyword to keep variables locked to scope like:
function number(busStops) {
let answer = 0;
for (let bs of busStops) {
answer += bs[0] - bs[1];
}
return answer;
}
I have a array in JavaScript like this.
var arr=
[
['A'],[1,2,3,4],
['A'],[4,3,2,1],
['B'],[10,12,3,1],
['B'],[1,2,3,4],
.
.
.
.
['AZ'],[1,2,3,4]
]
and I want the output to summarize the array like -
var output=
[
['A'],[5,5,5,5],
['B'],[11,14,6,5],
['AZ'],[1,2,3,4]
]
Thanks.
Script
You can use the following script to achieve what you want to do.
const arr = [
["A"],
[1, 2, 3, 4],
["A"],
[4, 3, 2, 1],
["B"],
[10, 12, 3, 1],
["B"],
[1, 2, 3, 4],
["AZ"],
[1, 2, 3, 4],
];
/**
* Generator to return key-value pairs with array[i] being the key and array[i+1] being the value
* #param {Array<any>} array
*/
function* keyValue(array) {
// make sure we can build pairs (other ways of handling this are also possible)
if (array.length % 2 !== 0)
throw new RangeError(
"Array length must be dividable by 2 without remainder!"
);
for (let i = 0; i < array.length; i += 2) {
yield [array[i], array[i + 1]];
}
}
// here the created key-value pairs
console.log("Key-value pairs created by keyValue() generator function:");
console.log([...keyValue(arr)]);
// loop over key value pairs and sum up all the individul arrays based on the letter assigned to them
const result = [...keyValue(arr)].reduce((all, [[key], array]) => {
// if we don't have values for this letter, assing copy of the array to that letter
if (!all[key]) all[key] = [...array];
// we have some values for that letter already, sum up each value
else all[key] = all[key].map((prev, idx) => prev + array[idx]);
return all;
}, {});
// this would be a "better" result to my mind as there is no point wrapping single string values in arrays
// When using objects the values can easily be accessed in O(1)
console.log(result);
// now transform JS object to array of arrays
console.log("Result:");
const transformed = Object.entries(result).flatMap(([key, value]) => [[key], value]);
console.log(transformed);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Please note: This implementation assumes that the arrays for a given letter have the same length (as is the case in your example).
Explanation
First of all, I use a generator function keyValue() to always group two consecutive values in the array (a key and a value) together. One could also do this differently but once you understand how generators work that's an easy and elegant approach, I think. For this demo I just throw an error if the array is not dividable by 2 without remainder, but one could also handle this more gracefully.
Then, using reduce(), I iterate over the array created by using keyValue() and for each element in the array I check if I've encountered that value before. If I have not, I create a copy of the array (for immutablility) and assign it to the key i.e. a letter. If I have encountered a certain letter before I add up the values that I have previously saved assigned to that letter with the ones I am currently processing. After iteration all sums are calculated and I have a JavaScript object containing the results.
To my mind, this would be a good output because your output is a bit odd, since there is no point storing single letters in an array or even arrays of arrays. Using a JavaScript object is much more convenient and faster for lookups.
Nevertheless, you can easily deduct your result from the created object using flatMap().
I'm trying to write a function decreasingOrder which takes a positive integer as input and return an array of its digits in decreasing order.
e.g., decreasingOrder(1234) Should give [4,3,2,1].
function decreasingOrder(n) {
let unarr = [...`${n}`].map(i => parseInt(i)); //Unordered Array of Digits
let oarr = []; //Ordered Array of Digits
for(let j=0; j<unarr.length; j++){
let max = Math.max.apply(Math, unarr);
oarr.push(max);
unarr.splice(unarr.indexOf(max), 1); //delete element from array
}
return oarr;
}
console.log(decreasingOrder(1234));
//Expected [4,3,2,1], Instead got [4,3]
I think, deleting element using splice method also reduces the number
of iteration.
I also tried delete operator but get [4, NaN, NaN, NaN] (because Math.max([undefined])).
When I tried with specific number instead of unarr.length in condition expression for for loop, it works fine!
So when I use splice method to delete elements it reduces the unarr.length and when I tried to keep unarr.length constant using delete operator it gives NaN, what should I do? Is there any other way to write to the same function? I'm beginner in JavaScript.
The issue in your code is unarr.splice(unarr.indexOf(max), 1) inside loop.
By taking your example of console.log(decreasingOrder(1234)). In the first cycle the highest number from the array is found and is removed from the array and pushed to new array.
At the end of the first cycle the outputs will be unarr = [1, 2, 3], oarr = [4] and j=1
Likewise after second loop unarr = [1, 2], oarr = [4, 3] and j=2. Now the loop condition j < unarr.length is not satisfied hence the loop breaks. So the output will be [4, 3].
Instead you can use the below utility for your requirement.
function decreasingOrder(n) {
let unarr = [...`${n}`].map(i => parseInt(i)) //Unordered Array of Digits
return unarr.sort((a,b) => b-a)
}
console.log(decreasingOrder(1234))
Hope this helps.
i'm working on a bigger problem but am a little stuck on a certain issue. Hopefully, I can explain it clearly! I am looking to generate an array of arrays where each individual array has elements then when added together equal a certain number. An example would be:
target = 4
solution : [[1,1,1,1], [1,1,2], [2,2], [1,3], [4]]
edit: to make the question more clear, the solution should contain every possible combination of positive integers that will equal the target
You could take a recursive approach and loop from the last found item or one and call the function until no more values are to disperse.
function x(count) {
function iter(left, right) {
if (!left) return result.push(right);
for (var i = right[right.length - 1] || 1; i <= left; i++)
iter(left - i, [...right, i]);
}
var result = []
iter(count, []);
return result;
}
x(4).map(a => console.log(...a));
I'm not sure what language you were working in. Also, it's general StackOverflow etiquette to show what you have already tried an what exact step you got stuck on. That said, here is some Python code that does what you want.
This problem is easy to solve as a recursive function. If you have some number n, the first number in a list of sums of n could be any number between 1 and n. Call that number i. Once it's picked, the rest of the list should sum to n - i. So just make a recursive function that adds up the results for all i's that are less than n and all the results for each of the solutions to n-i.
def get_sum_components(n):
# ignore negatives
if n <= 0:
raise ValueError("n must be a positive int")
# The only way to sum to 1 is [1]. This is the base case
if n == 1:
return [[1]]
results = []
# imagine that the first number in the list of sum components was i
for i in range(1, n):
remainder = n - i
# get_sum_components(remainder) will return a list of solutions for n-i
for result in get_sum_components(remainder):
# don't forget to add i back to the beginning so they sum to n
result = [i] + result
results.append(result)
# lastly, just the number itself is always an answer
results.append([n])
return results
print(get_sum_components(4))
# gives [[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2], [3, 1], [4]]
If you don't care about order, this will create some duplicates (like [1, 3], [3, 1]). It should be easy to filter those out, though. (Just sort all the lists and then use a dict/set to remove duplicates)
The MDN defines reduce() as below:
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
Let's not take anything for granted and just analyse the syntax of below statements:
const array1 = [1, 2, 3, 4];
const reducer = (y, x) => y + x;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
reducer is a function that takes two parameters and return their sum
reducer is executed on each element of the array, but 'each element of the array' is only ONE parameter, why can i assume that the SUM is the other parameter and cached somewhere waiting for the next add operation? and why can i assume reduce is returning the sum at the end?
Could someone answer these questions? From someone coming from other language background e.g. C which also has function concept. I am often confused by Javascript's syntax.
and that's how i get more confused when i see:
const pipeline = [
array => { array.pop(); return array; },
array => array.reverse()
];
pipeline.reduce((xs, f) => f(xs), [1, 2, 3]);
because again, according to MDN, The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
only this time,
reducer: (xs, f) => f(xs)
accumulator: [1, 2, 3]
array1: pipeline
then how do we explain its behaviour similar to our first example in English?
reducer is executed on each element of the array, but 'each element of the array' is only ONE parameter, why can i assume that the SUM is the other parameter and cached somewhere waiting for the next add operation?
The callback provided is called multiple times, once for each item in the array (or, for length - 1 times, in case no initial value is provided). You could easily implement this yourself:
const array1 = [1, 2, 3, 4];
const reducer = (y, x) => y + x;
Array.prototype.myReduce = function(callback, initialValue) {
let accum = initialValue === undefined
? this.shift()
: initialValue;
for (let i = 0; i < this.length; i++) {
accum = callback(accum, this[i], i, this);
}
return accum;
}
// 1 + 2 + 3 + 4
console.log(array1.myReduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.myReduce(reducer, 5));
Just because you pass one function to .reduce (or to any other function) doesn't put a limit on how many times that function can be called.
and why can i assume reduce is returning the sum at the end?
That's just how the method is defined - like with the implementation above, the accumulator (or accum) gets reassigned for every iteration, and passed to the next invocation of the callback.
The browser's native implementation of the method isn't actually written in Javascript like above, but its functionality is the same (for the most part).
The pipeline works the same way. For every element of the array provided, the accumulator is reassigned, and the next element is called with the new accumulator. Here, there's an array of functions which are being called, and each function's return value is being used as the next accumulator, and the value returned by the last function call is what the whole .reduce call resolves to.
const pipeline = [
array => { array.pop(); return array; },
array => array.reverse()
];
pipeline.reduce((xs, f) => f(xs), [1, 2, 3]);
Item 1: initial value (accumulator) is [1, 2, 3]. Plugging into array => { array.pop(); return array; } and you .pop() its last value (the 3, resulting in [1, 2], then you return the array.
Item 2: Accumulator (return value of last iteration) is [1, 2]. Plug it into array => array.reverse(), and you get the same array, reversed: [2, 1].
There are no more items in the array, so this [2, 1] is the value that the whole reduce call evaluates to.