I have got this block of code and I could not get the r.concat part because concat usually is used on a whole array not on a single element of it.
function doubleOddNumbers(numbers) {
return numbers.reduce((r, n) => n % 2 ? r.concat(n * 2) : r, [])
}
Here is the code annotated:
function doubleOddNumbers(numbers) {
return numbers.reduce( // reduce iterates over numbers and passes an accumulator from iteration to iteration
(r, n) => // the reducer function called for each element, r is the accumulator, n is the element
n % 2 // if the element is odd
? r.concat(n * 2) // then append its double to the accumulator
: r // otherwise return the accumulator unchanged
, []) // start with an empty array for the accumulator
}
Here is the MDN documentation on reduce and concat.
I think the missunderstanding comes from this usage of reduce:
[1, 2, 3].reduce((a, b) => a + b, 0); // 6
In this example, both the value of the array b, the accumulator a and the initial value 0 are numbers. But it doesn't have to be like this, the accumulator and the arrays values can have different types. If we change the line above to:
[1, 2, 3].reduce((a, b) => a + b, "") // "123"
As the initial accumulator is an empty string, the first time reduce executes it will concat "" + 1, which will result in "1" that gets passed to the next reduce step.
Now in your case, the initial accumulator value is an empty array.Therefore r will be an array, whilst n is a number. Now the reducer will either return r itself, or it will concatenate n * 2 to the array, which will also result in an array passed to the next reducer step.
[1, 2, 3].reduce((acc, el) => acc.concat(el), [])
That said, the code shown is just a complete missuse of the .reduce function. That you weren't able to understand the code does not mean that you are dumb, but it rather means that the code shown is badly written. I would write it as:
numbers
.filter(n => n % 2) // only.take odd numbers
.map(n => n * 2) // double them
As "numbers" is an array (of numbers), you can just start with the specification of the Array.reduce function here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
Every reduce works like so:
arrayToReduce.reduce((memo, currentElement) => { /* operations using the currentElement that return the new memo value */}, initialValue);
What happens:
You start with an initial value in memory (initialValue above) e.g. an empty array.
For each element of the array to reduce (e.g. arrayToReduce above), you execute a function which receives the current memorized value ("memo" above) and the current element in the array. The function will examine the current element and will compute a new memorized value. E.g. in your example, for odd numbers, you double the number and add it to the memorized array, then you return the memorized array; for even numbers you do nothing, so you return the memorized array unchanged.
The last value returned by the function is the eventual result of the reduce operation i.e. the array containing the odd numbers doubled.
Related
The code below is modified version of a code taken from the book Professional JavaScript for Web Developers.
// First argument is the type of array that should be returned
// Remaining arguments are all the typed arrays that should be concatenated
function numElements(typedArrayConstructor, ...typedArrays) {
// Count the total elements in all arrays
return typedArrays.reduce((x,y) => (x.length || x) + y.length);
}
console.log(numElements(Int32Array, Int8Array.of(1, 2, 3), Int16Array.of(4, 5, 6), Float32Array.of(7, 8, 9)));
My question is what does the (x.length || x) do? Why do we need to perform an or operation on x.length and x?
A little more explanation to go with Pointy's answer:
The || in JavaScript isn't just a logical OR operation, it deals with "truthy/falsey" values, not just booleans.
undefined is falsey. When the first operand of || is falsey, the second operand is evaluated, and becomes the result of the expression. Thus undefined || 0 equals 0.
In your sample code, this means when x is 0, you add 0, and get a proper numeric result. If you try to add to undefined to another number, all of your calculations turn into NaN after that.
When .reduce() is invoked with only one argument, the very first iteration uses element 0 as the first callback parameter and element 1 as the second.
In your case, that means that on the first iteration, x will be one of the arrays (note that y is always an array). Thus that little expression differentiates between when it's the first iteration and when it's a subsequent iteration by taking advantage of the fact that
someNumber.length
is always undefined. (As correctly noted in another answer, it's critical to recall that (undefined || x) will always be x, whatever its value may be.) On subsequent iterations therefore x is the running total of the array lengths.
The .reduce() could have been written as follows instead:
return typedArrays.reduce((x,y) => x + y.length, 0);
By passing the second argument (0) to .reduce(), the first callback invocation will be the same as the others, and x will always be a number.
If x has any elements and x exists then use the length in the sum. Otherwise if length is undefined then return the current element x
Example 1: - happens on first iteration of reduce loop
x is array [1, 2, 3]
x.length || x -> returns the length of array or current total
// summation of code would do the following
firsthArrayLength + secondArrayLength = newTotal
Example 2: - happens on rest of iterations of reduce loop
x is integer 5
x.length || x -> returns x since length of integer is undefined
// summation of code would do the following
currentTotal + curLength = newTotal
NOTE: Keep in mind that with this example if any of the arrays is null or undefined then it will throw since we cannot access property length of undefined or null
So sad that is taken from a book. Using reduce and || like that is reckless and unprofessional -
// First argument is the type of array that should be returned
// Remaining arguments are all the typed arrays that should be concatenated
const numElements = (constructor, ...arrs) =>
new constructor(arrs.reduce((r, a) => r + a.length, 0))
const result =
numElements
( Int32Array
, Int8Array.of(1, 2, 3)
, Int16Array.of(4, 5, 6)
, Float32Array.of(7, 8, 9)
)
console.log(result.constructor)
console.log(result)
// Int32Array
// { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
The description of the function says it should concat the other arrays, not just initialise an empty typed array. Here's what that might look like -
// First argument is the type of array that should be returned
// Remaining arguments are all the typed arrays that should be concatenated
const concatMixed = (constructor, ...arrs) =>
{ const r = new constructor(arrs.reduce((r, a) => r + a.length, 0))
let i = 0
for (const a of arrs)
for (const val of a)
r[i++] = val
return r
}
const result =
concatMixed
( Int32Array
, Int8Array.of(1, 2, 3)
, Int16Array.of(4, 5, 6)
, Float32Array.of(7.1, 8.2, 9.3)
)
console.log(result.constructor)
console.log(result)
// Int32Array
// { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
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.
I am studying Javascript and currently learning the .sort() function for arrays. I understood that it can take either no argument or one between a-b and b-a.
What I don't understand however is the use of return 0, return -1 and return 1. Here is an example (source: http://www.codewars.com/kata/572df796914b5ba27c000c90) :
var arr=[1,2,3,4,5,6,100,999]
arr.sort((a,b)=>{
if (a%2==b%2) return a-b;
if (a%2>b%2) return -1;
return 1;
})
console.log(arr)
//output: [ 1, 3, 5, 999, 2, 4, 6, 100 ]
I understand what it's supposed to do, i.e. separate odd and even numbers and sort them in ascending order. But what is the meaning of return -1 and return 1? Can someone walk me through this function step by step?
I tried to play with the code and change some values, for example change return -1 to return 0, to try to understand how it could work, but I still don't get it.
Where can I find resources with details about that return element?
According to the sort docs:
If the parameter functionComparison is supplied, the elements of the
array are sorted according to the return value of the comparison
function. If a and bare two elements to compare, then:
If functionComparison(a, b) is less than 0, we sort a with an index
less than b( a will be ranked before b)
If functionComparison(a, b) returns 0, we leave a and b unchanged
relative to each other, but sorted with respect to all the other
elements. Note: The ECMAScript standard does not guarantee this
behavior, so all browsers (eg Mozilla versions prior to 2003) do not
respect this. If functionComparison(a, b) is greater than 0, we sort b
with an index less than a.
functionComparison(a, b) must always return the same result from the
same pair of arguments. If the function returns inconsistent results,
then the order in which the items are sorted is not defined.
Now if a > b, returning 1 or a positive value is one and the same thing, similarly, if a < b then returning -1 or the difference is the same. If both are equal the difference is 0 and hence return 0
Where can I find resources with details about that return element?
return value is for the comparator function.
As per spec
If comparefn is not undefined, it should be a function that accepts
two arguments x and y and returns a negative value if x < y, zero if x
= y, or a positive value if x > y.
I'm reading a book called Eloquent JavaScript. There's an exercise in it that requires one to flatten a heterogeneous array & after trying so long and failing to get the answer, I looked up the solution online & couldn't understand the code. I'm hoping someone will be kind enough to explain, especially for argument "flat" and how it's supposed to work. The code is below
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
return flat.concat(current);
}, []));
The reduce function defined in the book is:
function reduce(array, combine, start) {
var current = start;
for (var i = 0; i < array.length; i++)
current = combine(current, array[i]);
return current;
}
and as a method of an array,
arr.reduce(combine, start);
Let's look at each part of the reduce method. The book describes it as "folding up the array, one element at a time." The first argument for reduce is the "combiner function", that accepts two arguments, the "current" value and the "next" item in the array.
Now, the initial "current" value is given as the second argument of the reduce function, and in the solution of flattening arrays, it is the empty array, []. Note that in the beginning, the "next" item in the array is the 0th item.
Quoting the book to observe: "If your array contains at least one element, you are allowed to leave off the start argument."
It may also be confusing that in the flattening solution, current is placed as the second argument to reduce, whereas in the reduce definition above, current is used to assign the cumulative, folded value. In the flattening solution, current refers to the "next" arrays item (the individual array of integers)
Now, at each step of the reduction, the "current" value plus the next array item is fed to the (anonymous) combiner, and the return value becomes the updated "current" value. That is, we consumed an element of the array and continue with the next item.
flat is merely the name given to the accumulated result. Because we wish to return a flat array, it is an appropriate name. Because an array has the concat function, the first step of the reduce function is, (pretending that I can assign the internal variables)
flat = []; // (assignment by being the second argument to reduce)
Now, walk through the reduction as iterating over arrays, by going through the steps shown above in reduce's definition
for (var i = 0; i < arrays.length; i++)
flat = combine(flat, arrays[i]);
Calling combine gives [].concat([1, 2, 3]) // => [1, 2, 3]
Then,
flat = [1, 2, 3].concat([4, 5]) // => [1, 2, 3, 4, 5]
and again for the next iteration of the reduction. The final return value of the reduce function is then the final value of flat.
This would be the solution I came with with ES6 format:
const reduced = arrays.reduce((result,array) => result.concat(array),[]);
console.log(reduced);
I have implemented this solution and this seems to work for nested arrays as well.
function flattenArray(arr){
for(var i=0;i<arr.length;i++){
if(arr[i] instanceof Array){
Array.prototype.splice.apply(arr,[i,1].concat(arr[i]))
}
}
return arr;
}
There is an easy way to do these exercises. those functions are already built inside the javascript so you can use them easily.
But the whole joy of this exercise is to create those functions:
Create reduce function. Reduce function should add all array elements. you can use a higher-order function or just a normal one. here is an example for higher-order:
function reduce(array, calculate){
let sumOfElements = 0;
for(let element of array){
sumOfElements = calculate(sumOfElements, element)
}
return sumOfElements
}
Next step is to create a concat function. since we need to return those reduced arrays in new array we will just return them. (Warning: you must use rest parameter)
function concat(...arr){
return arr
}
And for last. you will just display it (You can use any example)
console.log(concat(reduce([1, 2, 3, 4], (a, b) => a + b), reduce([5, 6], (a, b) => a + b)))
The reduce method acts as a for loop iterating over each element in an array. The solution takes each array element and concatenates it to the next one. That should flatten the array.
var arr =[[1,2],[3,4],[5,6]]
function flatten(arr){
const flat= arr.reduce((accumulator,currentValue)=>{
return accumulator.concat(currentValue)
})
return flat
}
console.log(flatten(arr))
//Output 1,2,3,4,5,6
var num_list = [1, 2, 3, 4];
function num_order(a, b) {return b-a; }
num_list.sort(num_order);
I've been through blogs and i have searched on this topic but to no avail. All describe this function to be sorting in descending order but none specify how does that happen..
For example, what values are stored in the parameters a and b and how are these values assigned.. Finally what results the function passes and how does sort method do to those values.. Any help would be appreciated..
Let me specify that there is a post similar to this but the answer in that post is not clear.. One of the user has provided a link here which makes it much clearer
The parameter you pass to the sort method is the comparison function. It will define the order the elements are sorted.
To see what values are being passed to the parameters a and b. Try this in your console:
var num_list = [1, 2, 3, 4];
num_list.sort(function(a, b) {
debugger;
return b-a;
})
MDN has good documentation on the compare function: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
Have a look at any sort algorithm : at some point they need to compare two elements of the array.
The sort function of most Browsers is heapsort or quicksort, but i will take bubble sort as an example of a sort algorithm :
n = array size
repeat
swapped = false
for i= 0 to n-2
if array [i] > array [i+1] then
swap ( array [i] , array [i+1] )
swapped = true
end for
n = n -1
until swapped = false
we can easily rewrite the comparison to use a comparison function :
n = array size
repeat
swapped = false
for i= 0 to n-2
a = array [i]
b = array [i+1]
if compareFunction(a,b) > 0 then
swap ( array [i] , array [i+1] )
swapped = true
end for
n = n -1
until swapped = false
with :
compareFunction (a,b)
return a-b
So the comparison function is just a function that returns an integer that reflect the items order.
If two elements are the same => the function returns 0, first is bigger => returns >0, second is bigger => returns <0.
Using a comparison function allows to sort any kind of array (i.e. an array containing any kind of item), while still using the very same sort algorithm as the one used for integer sort.