How do these extra parameters work with map()? - javascript

I'm working on better understanding functional programming in javascript, but I'm a bit confused by what I've seen fed to map functions. Take the example below:
const f = x => (a, b, c) => b + a;
const arr = [1, 2, 3, 4, 5, 6];
const m = arr.map(f(1));
document.write(m);
When f returns a it will print each value, as expected. If it returns b it seems to return the index, and c will return the entire array for each value. Is there a reason to why this function works this way?

Array.prototype.map() callback function has three default parameters
The current element of the iteration
The index of the current element of the iteration
The array that .map() function was called upon
f returns a function which is set as callback of .map()
See also Array.from()

In your example, You are invoking map with a 3-ary callback where:
a -> current element
b -> current index
c -> original array
and returning c. Therefore, your result will be a new array containing a reference to the original array for every element iterated over.
Since you aren't doing anything with x, there is no need for a nested function here. A better example of how you can use this concept would be something like:
const add = a => b => a + b
const arr = [1, 2, 3, 4]
const newArr = arr.map(add(3))
// [4, 5, 6, 7]
console.log(newArr)

Related

What is the reason array is available inside reduce(), map(), etc.?

In the following example, we have access to the array as numbers and arr. It seems more in line with functional programming to use the internal variable arr but what is an explicit reason why we should use it instead of the exterior variable, since, numbers and arr are both pointers to the same array value anyway.
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, m, index, arr) => {
console.log(`acc=${acc}, m=${m}, index=${index}, arr=${arr}`);
console.log(`acc=${acc}, m=${m}, index=${index}, numbers=${numbers}`);
return acc += m;
}, 100);
console.log(sum);
Because not every array will be stored in a variable. You can chain calls to map() & other, or after a call to a function that returns an array, in those cases you can access the array by variable name.
functionThatReturnsAnArray(...).map((acc, m, index, arr) => {
// We can only access the array because
//it was passed as an argument to the anonymous function
})

Write function outputs to file in javascript [duplicate]

Let's suppose I wanted a sort function that returns a sorted copy of the inputted array. I naively tried this
function sort(arr) {
return arr.sort();
}
and I tested it with this, which shows that my sort method is mutating the array.
var a = [2,3,7,5,3,7,1,3,4];
sort(a);
alert(a); //alerts "1,2,3,3,3,4,5,7,7"
I also tried this approach
function sort(arr) {
return Array.prototype.sort(arr);
}
but it doesn't work at all.
Is there a straightforward way around this, preferably a way that doesn't require hand-rolling my own sorting algorithm or copying every element of the array into a new one?
You need to copy the array before you sort it. One way with es6:
const sorted = [...arr].sort();
The spread-syntax as array literal (copied from mdn):
var arr = [1, 2, 3];
var arr2 = [...arr]; // like arr.slice()
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
Just copy the array. There are many ways to do that:
function sort(arr) {
return arr.concat().sort();
}
// Or:
return Array.prototype.slice.call(arr).sort(); // For array-like objects
Try the following
function sortCopy(arr) {
return arr.slice(0).sort();
}
The slice(0) expression creates a copy of the array starting at element 0.
You can use slice with no arguments to copy an array:
var foo,
bar;
foo = [3,1,2];
bar = foo.slice().sort();
You can also do this
d = [20, 30, 10]
e = Array.from(d)
e.sort()
This way d will not get mutated.
function sorted(arr) {
temp = Array.from(arr)
return temp.sort()
}
//Use it like this
x = [20, 10, 100]
console.log(sorted(x))
Update - Array.prototype.toSorted() proposal
The Array.prototype.toSorted(compareFn) -> Array is a new method which was proposed to be added to the Array.prototype and is currently in stage 3 (Soon to be available).
This method will keep the target Array untouched and returns a copy of it with the change performed instead.
Anyone who wants to do a deep copy (e.g. if your array contains objects) can use:
let arrCopy = JSON.parse(JSON.stringify(arr))
Then you can sort arrCopy without changing arr.
arrCopy.sort((obj1, obj2) => obj1.id > obj2.id)
Please note: this can be slow for very large arrays.
Try this to sort the numbers. This does not mutate the original array.
function sort(arr) {
return arr.slice(0).sort((a,b) => a-b);
}
There's a new tc39 proposal, which adds a toSorted method to Array that returns a copy of the array and doesn't modify the original.
For example:
const sequence = [3, 2, 1];
sequence.toSorted(); // => [1, 2, 3]
sequence; // => [3, 2, 1]
As it's currently in stage 3, it will likely be implemented in browser engines soon, but in the meantime a polyfill is available here or in core-js.
I think that my answer is a bit too late but if someone come across this issue again the solution may be useful.
I can propose yet another approach with a native function which returns a sorted array.
This code still mutates the original object but instead of native behaviour this implementation returns a sorted array.
// Remember that it is not recommended to extend build-in prototypes
// or even worse override native functions.
// You can create a seperate function if you like
// You can specify any name instead of "sorted" (Python-like)
// Check for existence of the method in prototype
if (typeof Array.prototype.sorted == "undefined") {
// If it does not exist you provide your own method
Array.prototype.sorted = function () {
Array.prototype.sort.apply(this, arguments);
return this;
};
}
This way of solving the problem was ideal in my situation.
You can also extend the existing Array functionality. This allows chaining different array functions together.
Array.prototype.sorted = function (compareFn) {
const shallowCopy = this.slice();
shallowCopy.sort(compareFn);
return shallowCopy;
}
[1, 2, 3, 4, 5, 6]
.filter(x => x % 2 == 0)
.sorted((l, r) => r - l)
.map(x => x * 2)
// -> [12, 8, 4]
Same in typescript:
// extensions.ts
Array.prototype.sorted = function (compareFn?: ((a: any, b: any) => number) | undefined) {
const shallowCopy = this.slice();
shallowCopy.sort(compareFn);
return shallowCopy;
}
declare global {
interface Array<T> {
sorted(compareFn?: (a: T, b: T) => number): Array<T>;
}
}
export {}
// index.ts
import 'extensions.ts';
[1, 2, 3, 4, 5, 6]
.filter(x => x % 2 == 0)
.sorted((l, r) => r - l)
.map(x => x * 2)
// -> [12, 8, 4]

Calculate the mathematical difference of each element between two arrays

Given two array of same length, return an array containing the mathematical difference of each element between two arrays.
Example:
a = [3, 4, 7]
b = [3, 9, 10 ]
results: c = [(3-3), (9-4), (10,7)] so that c = [0, 5 3]
let difference = []
function calculateDifferenceArray(data_one, data_two){
let i = 0
for (i in data_duplicates) {
difference.push(data_two[i]-data_one[i])
}
console.log(difference)
return difference
}
calculateDifferenceArray((b, a))
It does work.
I am wondering if there is a more elegant way to achieve the same
Use map as following:
const a = [3, 4, 7]
const b = [3, 9, 10]
const c = b.map((e, i) => e - a[i])
// [0, 5, 3]
for-in isn't a good tool for looping through arrays (more in my answer here).
"More elegant" is subjective, but it can be more concise and, to my eyes, clear if you use map:
function calculateDifferenceArray(data_one, data_two){
return data_one.map((v1, index) => data_two[index] - v1)
}
calculateDifferenceArray(b, a) // < Note just one set of () here
Live Example:
const a = [3, 4, 7];
const b = [3, 9, 10 ];
function calculateDifferenceArray(data_one, data_two){
return data_one.map((v1, index) => v1 - data_two[index]);
}
console.log(calculateDifferenceArray(b, a));
or if you prefer it slightly more verbose for debugging et. al.:
function calculateDifferenceArray(data_one, data_two){
return data_one.map((v1, index) => {
const v2 = data_two[index]
return v1 - v2
})
}
calculateDifferenceArray(b, a)
A couple of notes on the version of this in the question:
It seems to loop over something (data_duplicates?) unrelated to the two arrays passed into the method.
It pushes to an array declared outside the function. That means if you call the function twice, it'll push the second set of values into the array but leave the first set of values there. That declaration and initialization should be inside the function, not outside it.
You had two sets of () in the calculateDifferenceArray call. That meant you only passed one argument to the function, because the inner () wrapped an expression with the comma operator, which takes its second operand as its result.
You had the order of the subtraction operation backward.
You could use higher order array method map. It would work something like this:
let a = [2,3,4];
let b = [3,5,7];
let difference = a.map((n,i)=>n-b[i]);
console.log(difference);
you can read more about map here

How are parameters get mapped in reduce function?

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.

Does JS support sorting with a key function, rather than a comparator?

JavaScript's array.sort method takes an optional compare function as argument, which takes two arguments and decides which one of them is smaller than the other.
However, sometimes it would be more convenient to customize the sort order with a key function, which is a function that takes one value as an argument and assigns it a sort key. For example:
function keyFunc(value){
return Math.abs(value);
}
myArr = [1, 3, -2];
myArr.sort(keyFunc);
// the result should be [1, -2, 3]
Does JavaScript have support for this, or is there no way around writing a full-blown comparison function?
There's no support for exactly what you describe, but it's quite trivial to write a standard .sort function that achieves the same thing, with minimal code - just return the difference between calling keyFunc on the two arguments to sort:
function keyFunc(value){
// complicated custom logic here, if desired
return Math.abs(value);
}
myArr = [1, 3, -2];
myArr.sort((a, b) => keyFunc(a) - keyFunc(b));
console.log(myArr);
// the result should be [1, -2, 3]
If the key function is complicated and you don't want to run it more than necessary, then it would be pretty simple to create a lookup table for each input, accessing the lookup table if keyFunc has been called with that value before:
const keyValues = new Map();
function keyFunc(value){
const previous = keyValues.get(value);
if (previous !== undefined) return previous
console.log('running expensive operations for ' + value);
// complicated custom logic here, if desired
const result = Math.abs(value);
keyValues.set(value, result);
return result;
}
myArr = [1, 3, -2];
myArr.sort((a, b) => keyFunc(a) - keyFunc(b));
console.log(myArr);
// the result should be [1, -2, 3]
As stated already you have to write that functionality yourself or extend the current array sort method etc.
Another approach is if you ware using lodash and its orderBy method ... then this becomes:
myArr=[1, 3, -2];
const result = _.orderBy(myArr, Math.abs)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could use a closure over the wanted function.
const
keyFunc = value => Math.abs(value),
sortBy = fn => (a, b) => fn(a) - fn(b),
array = [1, 3, -2];
array.sort(sortBy(keyFunc));
console.log(array); // [1, -2, 3]
You can easily subtract the "keys" from the two elements:
myArr.sort((a, b) => keyFunc(a) - keyFunc(b));
You could also monkey patch sort:
{
const { sort } = Array.prototype;
Array.prototype.sort = function(sorter) {
if(sorter.length === 2) {
sort.call(this, sorter);
} else {
sort.call(this, (a, b) => sorter(a) - sorter(b));
}
};
}
So then:
myArr.sort(keyFunc);
works.

Categories