Understanding Arrow functions with an Example - javascript

I'm having trouble understanding a solution to a coding challenge that uses arrow functions and the .every() method and was hoping someone more knowledgeable could explain what's going on. The challenge is to check a grid to see if it represents a true sudoku board. I understand the first part concatenates and multiplies a row/col/square, but can't understand the latter part...
// True, if product of #s in row is 9!
p = a => eval(a.join("*")) == (1*2*3*4*5*6*7*8*9);
// Check each row, column and 3 block grid using p()
sudoku = grid =>
grid.every((r,i) =>
p(r) &&
p(grid.map(r => r[i])) &&
p(r.map((_,j) => grid[3*(i/3|0)+(j/3|0)][3*(i%3)+(j%3)]) ) )
Thank you for the help!

Seems input data for sudoku is 2d array like this one:
let grid = [
[2,4,1,7,6,8,5,3,9],
[5,7,3,9,2,4,1,8,6],
[8,9,6,5,3,1,7,4,2],
[7,3,4,2,9,5,6,1,8],
[1,8,9,4,7,6,3,2,5],
[6,5,2,8,1,3,4,9,7],
[4,6,5,3,8,2,9,7,1],
[3,2,7,1,5,9,8,6,4],
[9,1,8,6,4,7,2,5,3]
]
To validate Sudoku we have to check all rows, columns and sub-grids
Code explanation:
p = a => eval(a.join("*")) == (1*2*3*4*5*6*7*8*9);
This is an equivalent for
p = function(a) {
return eval(a.join("*")) == (1*2*3*4*5*6*7*8*9);
}
So function "p" gets array of integers
[2,4,1,7,6,8,5,3,9]
joins all integers with "*" and as a result we have:
"2*4*1*7*6*8*5*3*9"
then evaluates this string and as a result we have:
362880
This is the same value as 1*2*3*4*5*6*7*8*9
So now we can check each row, each column and each sub-grid with this function.
sudoku = grid =>
grid.every((r,i) =>
validRows &&
validColumns &&
validSubGrids )
is the same as:
sudoku = function(grid) {
grid.every( function(r, i) {
return
validRows &&
validColumns &&
validSubGrids
})
}
The every method executes the provided callback function once for each element present in the array until it finds one where callback returns a falsy value. Otherwise it returns true.
Valid Sudoku means that our every function's callback returns true for all elements.
p(r) - validate each row
p(grid.map(r => r[i])) - validate each column
function map creates new array.
E.g. for i=0 it will provide following result:
[ grid[0][0], grid[0][1], grid[0][2],...]
p(r.map((_,j) => grid[3*(i/3|0)+(j/3|0)][3*(i%3)+(j%3)]) ) ) - validate each sub-grid
Code equivalent:
p(
r.map( function(_,j) {
let row = 3 * Math.floor(i/3) + Math.floor(j/3)
let column = 3 * (i%3) + (j%3)
return grid[row][column]
})
)
So as a result we validated all rows, all columns and all sub-grids.

For the most part, an arrow function is just a function. The only real different between the two is that this is preserved from the outer context when using an arrow function, whereas this is the this that was used to call the function with a normal function.
function A() {
this.v = 1;
this.a = () => console.log(this.v);
this.b = function () { console.log(this.v) };
}
// Both will use `obj` as `this`
const obj = new A();
obj.a();
obj.b();
// The arrow-function `a()` will keep the same `this` as above, `obj` will change.
const obj2 = { v: 2 };
obj2.a = obj.a;
obj2.b = obj.b;
obj2.a();
obj2.b();
The other difference which may be throwing you off, is with an arrow function, if it only needs one line, you can omit the curly brackets ({}) and it will return the value. I.e.,
a => 1
Is the same as:
function (a) { return 1; }
In this sample code, being an arrow function or a regular function would make no different, as this isn't being invoked.
What the code is doing is:
grid.every((r, i) =>
Look through every element in the grid, and keep going as long as it returns true. r is the current value of grid and i is the current index it is processing.
grid.map(r => r[i])
is literally just getting the i-th value from r and returning it. This would be for checking the diagonals of a two-dimensional array. (So it gets grid[0][0], grid[1][1], and so on).
r.map((_, j) => grid[math])
Is then just looping through each element in r, and getting some element (based on that math) using the current i index from the outer loop and the j index from r. Using _ as a parameter name is a common convention to indicate that you just don't care about that parameter.

sudoku = grid =>
grid.every((r,i) =>
p(r) &&
p(grid.map(r => r[i])) &&
p(r.map((_,j) => grid[3*(i/3|0)+(j/3|0)][3*(i%3)+(j%3)]) ) )
For every item r in grid, p(r) is evaluated. If its true then second part will be evaluated
grid.map(r => r[i]) will be evaluated which in turn will iterate through all grid items and return an array of their ith element.
Now after if p(grid.map(r => r[i])) is true then the latter part will be evaluated.
Finally r.map((_,j) => grid[3*(i/3|0)+(j/3|0)][3*(i%3)+(j%3)]) ) will be executed but only if previous two conditions were true.This will also return an array of grid items, depending upon values of i & j.
So key points here is the usage of &&
A && B will return B if A evaluates to true(and B will only be evaluated when A is true)

Related

What means the comparison between empty parantheses without a function and content of curly brackets [duplicate]

I'm new to both ES6 and React and I keep seeing arrow functions. Why is it that some arrow functions use curly braces after the fat arrow and some use parentheses?
For example:
const foo = (params) => (
<span>
<p>Content</p>
</span>
);
vs.
const handleBar = (e) => {
e.preventDefault();
dispatch('logout');
};
The parenthesis are returning a single value, the curly braces are executing multiple lines of code.
Your example looks confusing because it's using JSX which looks like multiple "lines" but really just gets compiled to a single "element."
Here are some more examples that all do the same thing:
const a = (who) => "hello " + who + "!";
const b = (who) => ("hello " + who + "!");
const c = (who) => (
"hello " + who + "!"
);
const d = (who) => (
"hello "
+ who
+ "!"
);
const e = (who) => {
return "hello " + who + "!";
};
You will also often see parenthesis around object literals because that's a way to avoid the parser treating it as a code block:
const x = () => {} // Does nothing
const y = () => ({}) // returns an object
One can also use curly braces to prevent a single line arrow function from returning a value -- or to make it obvious to the next developer that a single line arrow function shouldn't, in this case, be returning anything.
For example:
const myFunc = (stuff) => { someArray.push(stuff) }
const otherFunc = (stuff) => someArray.push(stuff)
console.log(myFunc()) // --> logs undefined
console.log(otherFunc()) // --> logs result of push which is new array length
Parenthesis are used in an arrow function to return an object.
() => ({ name: 'YourName' }) // This will return an object
That is equivalent to
() => {
return { name : 'YourName' }
}
Actually in a briefcase when somebody uses braces in an arrow function declaration, it is equal to below:
const arrow = number => number + 1;
|||
const arrow = (number) => number + 1;
|||
const arrow = (number) => ( number + 1 );
|||
const arrow = (number) => { return number + 1 };
Parenthesis has an implicit return statement while curly braces you need an explicit return statement
If you use curly braces after the arrow to define the function body, you have to use the 'return' keyword to return something.
For example:
const myFun1 = (x) => {
return x;
}; // It will return x
const myFun2 = (x) => {
x;
}; // It will return nothing
If you use the parenthesis, you don't need to mention the 'return' keyword.
For example:
const myFunc1 = (x) => x; // It will return x
const myFunc2 = (x) => (x); // It will also return x
In your first example, the right-hand side of the arrow function shows a single expression that is enclosed by a grouping operator:
const foo = (params) => (
<span>
<p>Content</p>
</span>
);
A similar comparable case would be the following:
const foo = (params) => (<span><p>Content</p></span>);
A distinction, in the above cases using single expressions, is that the right-hand side is the returned value of the function.
On the other hand, if you use curly braces, JavaScript will understand that as a statement:
const foo = (params) => {} // this is not an object being returned, it's just an empty statement
Therefore, using statement is a good start for you to have code in it, multiple lines, and it will require the use of "return" if the function is intended to return value:
const foo = (params) => {
let value = 1;
return value;
}
In case you wanted to return an empty object in the shortest form:
const foo = (params) => ({})
See tests
To answer a duplicate post(question posted here), just for reference for others:
var func = x => x * x;
// concise body syntax, implied "return"
var func = (x, y) => { return x + y; };
// with block body, explicit "return" needed
For reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Function_body
Also note:
If you are returning an object literal as the result from a fat arrow function, then you must enclose the object in parentheses, e.g., myFunc = () => ({ data: "hello"}). You will receive an error if you omit the parentheses because the build tools will assume that the curly braces of the object literal are the start and end of a function body.
Every function has 2 aspects.
First of them is that each one, not just the arrow functions, has an execution context (a block scope) in which the variables are created and used.
In other words, inside the curly braces { ... } of the function, what is declared and assigned there, stays there and is not visible to the outside functions / or variables.
For example, when writing something as
let x = 100;
function doSomething() {
let x = 50;
console.log(x);
}
doSomething(); // 50
console.log(x); // 100
both values are displayed in console (instead of 'x from outside just being replaced by x from inside the function').
You see that despite of let not usually allowing other variable x to be declared again (with the same name x), in this case, because the second x is declared and initialized inside the { ... }, it does not alter the outside one, which also happens because after the function doSomething is called, the x from inside of it is created, assigned, printed in console and then destroyed (deleted from the memory). So that process happens every time we call that function by running doSomething() .
So this is the first aspect to take into consideration when understanding the functions: they execute then forget the values created by the code inside their curly braces.
Because of it, it's easier to understand their second aspect -- as functions cannot just work isolated from the others, they need to also send data to the others, so they have some 'reporting aspect' used to externalize some part of the results computed inside their curly braces, which is exactly why the return statement exists.
Return exists in each function, even in the console.log or alert(), even in doSomething(), but in these cases where we didn't explicitly set something for it, it is always 'return undefined'.
Therefore it isn't necessary to write it, but instead know that where you don't return something specific, the function itself will do it for you by returning undefined.
When you write (or use) a function meant just to execute something, it will also return undefined. Always.
You can check that thing with every function which (apparently) has no declared return:
let x = alert(100);
console.log(x); // undefined
let y = doSomething(); // console prints 50
console.log(y); // 50, then undefined --- 2 lines
console.log(alert('Hello')); // undefined
console.log(console.log('Okay')); // Okay , then undefined
Why is that?
Because alert() which is a method of global object window (in browser) (so it is actually window.alert() ) and also console.log() (which is the same with window.console.log() , too), execute something (printing in an alert box or in the console whatever is in between the () AND THEN return undefined).
Now, coming back to the arrow functions, they are not just some new way of notation for writing the functions but they also have some specific features.
First, if you only have a parameter between the () in an arrow function, you can write it without the parentheses.
Second, if inside the curly braces there's a single statement, you can omit as well the curly braces.
Third one, if the single statement is a return statement, you can omit the word return.
Somehow, using these we could transform many usual functions into arrow functions if needed:
function doSomething() {let x = 50; console.log(x);} // as function declaration
let doSomething = function() {let x = 50; console.log(x);}; // as function expression, which is an anonymous function assigned to the variable 'doSomething'
let doSomething = () => {let x = 50; console.log(x);}; // as arrow function
// let's transform it further
let doSomething = () => {console.log(50)}; //
// that is equivalent to ---- let doSomething = () => {console.log(50); return undefined};
// or even to ---- let doSomething = () => {return ( console.log(50) ) };
// because anyways, *console.log* has *return undefined* in it, as explained above
//which is the same as ---- let doSomething = () => {return console.log(50) };
// let's now apply the rules 2 and 3 from above, one by one:
let doSomething = () => return console.log(50);
let doSomething = () => console.log(50);
// Obviously, this just shows how we could rewrite many usual functions (functions declarations) into arrow functions
// we can do that safely if we don't have any **this** involved in the functions, of course
// also, from all lines of code above only one must remain, for example the last one.
// the last one, despite only having ---- console.log(50) --- as the execution aspect, it also ---- returns undefined ---- as well
// obviously ---- console.log( typeof doSomething ); // function
// while ---- console.log( typeof doSomething() ); // undefined
If an arrow function has 2 or more parameters, we cannot omit the parentheses around them:
function sum(a, b) {let total = a + b; return total}
let sum = function(a, b) {let total = a + b; return total};
// or
let sum = (a, b) => {let total = a + b; return total};
// or
let sum = (a, b) => {return a + b};
// or
let sum = (a, b) => a + b;
For simple operations as above, the fat arrow sign '=>' can be "read" as is transformed into, in other words, a and b is (are) transformed into a + b.
Opposite to that, there are also functions that validate some data (for example checking the data type, etc), like this one
let isNumber = x => typeof x === "number";
// or
let isNumber = (x) => {return (typeof x === "number")};
// obviously,
isNumber("Hello, John!"); // false
Those DON'T transform the data, and thus the arrow sign can be read something more as with the condition that, or similar.
In other words, a function like
let double = x => x * 2 // 'double' is a function that transforms x into x*2
is not the same as a checking one (mostly used in filters, sort, and other kind of validating functions, usually as callback function, etc)
let isArray = arr => Array.isArray(arr) // that last one already returns boolean by itself, no need to write return (Array.isArray() etc)
Last thing to know about return is that when you write code in multiple lines, the ASI (Automatic Semicolon Insertion) will insert a ';' after return if you mistakenly press enter after writing the return word, which will break the code, therefore instead of
return
a+b;
your code will behave as
return;
a+b;
so you better write the code with parentheses as here:
return (
a + b
);
as explained in MDN website here, too.

how to group an array based on a specific function?

Here, I am new to JavaScript. I am solving questions; however, I am having a problem understanding chaining more than one method together. I have been trying to understand this solution, but it took me a lot of time, and I still don't get it.
I understand that I will input the array that I needed to change according to the specific function, which I opted. I understand all of methods functions, but I don't understand their syntax here, so can someone please explain each step to me ?
const group_By = (arr, fn) =>
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => {
acc[val] = (acc[val] || []).concat(arr[i]);
return acc;
}, {});
In as few words as possible.
Firstly they compute a ternary expression, here they are checking if the input is a function, if it is they pass it as is, otherwise they create an anonymous function that tries to access the given property. The arrow function after the colon can seem a little confusing but it's still just a function. It takes one argument called val, and returns property which key is inside the fn variable.
typeof fn === 'function' ? fn : val => val[fn]
The next step is to create a new array with new values for each of the elements. Output of this step is just a list of values to group elements on.
For instance calling it on array ["a", "bb"] with a fn='length' would return [1,2]
arr.map(typeof fn === 'function' ? fn : val => val[fn])
Then they call the .reduce function on the output array. The purpose of the reduce function is to create a single value out of all the elements slowly iterating over it. You can tell that the last step returns accumulator value back, and that it is passed as a first argument to the function called on the next element. The empty object at the end is just an initial value of the accumulator.
.reduce((acc, val, i) => {
...
return acc;
}, {});
And finally for the step that does the accumulation. Here firstly the val from the result of the map, is used to access property of the newly created object. If the value does not exist it replaced with an empty array || []. That has the element of the initial array at the same index concatenated onto it. If there were some elements it just adds new ones to it and reassigns the value.
acc[val] = (acc[val] || []).concat(arr[i]);
Okay, what I understood from your query is that you are trying to chain multiple functions together.
function Chained() {
// this reference.
const _this_ = this
this.Function1 = () => // do something and return _this_
this.Function2 = () => // do something here and return _this_
}
Above you can see that chain is a simple object which returns "this" as context. and on context, we already have Function1 and Function2. so due to this following will be valid.
const _ExecuteChained = new Chained()
// Now you can run _ExecuteChained.Function1().Function2() and so on.
Now coming to your code.
const group_By = (arr, fn) =>
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val,
i) => {
acc[val] = (acc[val] || []).concat(arr[i]);
return acc;
}, {});
Here you are just running a loop on arr and validating if the second param is a function or not if function then return it as is (Since you are using a map it will be stored at the given index. else just get the value of the function at the given key and return it.
Reduce.
in Reduce you are trying to accumulate a given value with (contact or merge or extend) value at a given index of arr in this case function.

JavaScript function return False and 0 respectively

I am new to JavaScript and I am trying to write functional Programming to calculate if a number is Odd (isOdd) or not. I do not understand why I've got true and 0 or false and 1 when I called isOdd() twice in a row.
here is my code:
var mod = m => {return number => {return number % m }}
var eq = number1 => {return number2 => {return number1 === number2}}
var combine = (...fns) => { return arg => {for(let fn of fns.reverse()) arg = fn(arg); return arg;}}
var eq1 = eq(1)
var mod2 = mod(2)
var isOdd = combine(eq1, mod2)
mod2(4) -----> returns 0
mod2(3) -----> returns 1
eq1(1) -----> returns true
eq1(2) -----> returns false
isOdd(1) returns true
isOdd(1) returns 1 (what??)
I can not understand what I missed or what goes wrong. I tested this code in most browsers.
Appreciate if someone can explain in details.
I can wrap isOdd again and have something like
isOdd = (number) => {return Boolean(combine(eq1, mod2))}
that returns boolean value everytime. But I want to understand what I missed in the first place/
Array.prototype.reverse reverses the fns array in-place. You’re calling .reverse on fns every time you call isOdd, even though you pass a fixed argument list of functions in combine once. That list is scoped to combine and reversed on every call of isOdd(1).
In other words, your first isOdd(1) call remembers the fns array of your original combine(eq1, mod2) call. fns is [ eq1, mod2 ]. Then you call fns.reverse() to iterate over it; but this mutates fns to [ mod2, eq1 ]. You get the correct result, because you wanted to call the functions in reverse order. The resulting call is eq1(mod2(1)) === eq1(1) === true.
In your second isOdd(1) call, however, fns is still remembered, and it still has the reversed [ mod2, eq1 ] value, because its scope is combine, so another isOdd call doesn’t reset the originally passed fns. The second isOdd call reverses this array again because fns.reverse is called within isOdd. The resulting call is mod2(eq1(1)) === mod2(true) === 1 (because true % 2 is coerced to 1 % 2).
A working function would look like this:
const combine = (...fns) => {
const reversedFns = fns.reverse();
return (arg) => {
for(let fn of reversedFns){
arg = fn(arg);
}
return arg;
};
};
A simpler approach uses Array.prototype.reduceRight:
const combine = (...fns) => (arg) => fns.reduceRight((result, fn) => fn(result), arg);
Since you want to aggregate multiple operations onto a single result based on a list from right to left, reduceRight is the perfect method for this. You start with arg, go through the list of functions from right to left, then call the current function with arg, and that becomes the new arg for the next function. The final result is returned. All this is captured by the code fns.reduceRight((result, fn) => fn(result), arg).

.filter(v => !!v) in Javascript

I have an application that can turns a tex file into a JavaScript object, with key-value pairs. The key being the word and the value being the number of times it has appeared in the text file. Let's go through it together:
FormatText.prototype.toDowncase = function() {
return this._data = this._data.toLowerCase();
};
This turns the words to lowercase
FormatText.prototype.deleteWords = function() {
return this._data = this._data.replace(/\W/g, " ");
};
This replaces all non-words with a space
FormatText.prototype.splitWords = function() {
return this._data = this._data.split(/\s+/);
};
This turns the string in an array and splits at each delimiter
FormatText.prototype.filterEntries = function() {
return this._data = this._data.filter(v => !!v);
};
This one above I have no clue what it does.
FormatText.prototype.countWords = function() {
return this._data = this._data.reduce((dict, v) => {dict[v] = v in dict ? dict[v] + 1 : 1; return dict}, {});
}
Could someone explain this one, however I will get it a try:
This one takes the array and passed the method 'reduce' with two arguments. It counts how many times each individual word has appeared and returns an object with the 'key-value' pairs described at the beginning of this question.
v => !!v means take v, and coerce it to a Boolean type by applying NOT twice. So the filter function is basically removing any falsey values (0, null, undefined) from this._data.
countWords is counting the number of times each word occurs in this._data - it is going through the array and adding 1 to the count if the word has been encountered before, or returning 1 if the word has not been encountered before. It returns an object with the words as keys and the counts as values.
As a note, these functions change the type of this._data, from a string, to an array, to an object. That may cause bugs to appear if e.g. you run the same method twice
Why not just return the value, without NOT NOT, like
v => v
because for filtering the value coerces to a boolean value.
From Array#filter:
Description
filter() calls a provided callback function once for each element in an array, and constructs a new array of all the values for which callback returns a value that coerces to true. callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values. Array elements which do not pass the callback test are simply skipped, and are not included in the new array.
In this case the double exclamation mark is useless: the value returned from the callback in filter(callback) is then coerced to a boolean automatically, so no need to do it using double exclamation mark. The following lines are equivalent:
.filter(v => !!v)
.filter(v => v)
.filter(Boolean)
This one above I have no clue what it does.
The javascript operator ! (logical not) performs a type coercion (to boolean) on its argument. So applied twice you somehow convert any type to a boolean value which gives you whether it is falsy or truthy.
This is interesting when you want to apply a condition to different types whose semantic is more or less "no value". For example:
!!('') //false
!!(0) //false
!!null //false
!!undefined //false
Could someone explain this one, however I will get it a try
reduce is method of the array prototype which allows to iterate over a collection while aggregating value.
In your specific example the aggregator is a dictionary which maps a word to a count (number of appearance). So if the word is not present in the dictionary it creates a key for this word with a counter initialized to 1 otherwise it increments the counter (if word already present).
A equivalent could be
const countWords = function (words = [], dictionary = {}) {
if(words.length === 0) {
return dictionary;
}
const word = words.pop(); //remove and read the word at the end of the array
if(word in dictionary) {//if key is present in the dictionary
dictionary[word] += 1; // increment
else {
dictionary[word] = 1; // start a counter for new keyword
}
return countWords(words, dictionary);
}

Using extra callback parameters to Array.prototype.some

Do you have any real-world example of the use of the second and third parameters for the callback to Array.prototype.some or Array.prototype.any?
According to MDN:
callback is invoked with three arguments: the value of the element, the index of the
element, and the Array object being traversed.
I've personally never used them.
I have been working for some time on the Javascript functional programming library, Ramda, and early on we made the controversial decision not to use the index and array parameters for other similar functions that we created. There are good reasons for this, which I don't need to get into here, except to say that for some functions, such as map and filter, we find such extra parameters do have some occasional utility. So we offer a second function which supplies them to your callback. (For example, map.idx(yourFunc, list).)
But I've never even considered doing so for some or every. I never imagined a practical use of these. But there is now a suggestion that we include these functions in our list of index-supporting ones.
So my question again is whether you have ever found an actual, live, real-world callback function to some or every which actually needs these parameters? If so, could you describe it?
Answers of "No, I never do," would be helpful data too, thanks.
Quick search in our code:
function isAscending(array) {
return array.every(function (e, idx, arr) {
return (idx === 0) ? true : arr[idx-1] <= e;
});
}
I could imagine something like the following code to check whether an array is duplicate-free:
….every(function(v, i, arr) {
return arr.indexOf(v, i+1) == -1;
})
Where … is a complex expression so that you'd really have to use the arr parameter - which is no more an issue if you'd properly factor out the functionality in an own function that takes the array as an argument.
The second parameter can be useful sometimes, but I support your position that it is rather seldom used.
Yes, they are helpful
These extra parameters actually do come in handy, but not that often.
In the recent past, I had written a function to find all the permutations of a list of elements:
permute :: [a] -> [[a]]
For example permute [1,2,3] would be:
[ [1,2,3]
, [1,3,2]
, [2,1,3]
, [2,3,1]
, [3,1,2]
, [3,2,1]
]
The implementation of this function is quite simple:
If the input is [] then return [[]]. This is the edge case.
If the input is say [1,2,3]:
Add 1 to every permutation of [2,3].
Add 2 to every permutation of [1,3].
Add 3 to every permutation of [1,2].
Of course, the function is recursive. In JavaScript, I implemented it as follows:
var permute = (function () {
return permute;
function permute(list) {
if (list.length === 0) return [[]]; // edge case
else return list.reduce(permutate, []); // list of permutations
// is initially empty
}
function permutate(permutations, item, index, list) {
var before = list.slice(0, index); // all the items before "item"
var after = list.slice(index + 1); // all the items after "item"
var rest = before.concat(after); // all the items beside "item"
var perms = permute(rest); // permutations of rest
// add item to the beginning of each permutation
// the second argument of "map" is the "context"
// (i.e. the "this" parameter of the callback)
var newPerms = perms.map(concat, [item]);
return permutations.concat(newPerms); // update the list of permutations
}
function concat(list) {
return this.concat(list);
}
}());
As you can see, I have used both the index and the list parameters of the permutate function. So, yes there are cases where these extra parameters are indeed helpful.
However, they are also problematic
However these superfluous arguments can sometimes be problematic and difficult to debug. The most common example of this problematic behavior is when map and parseInt are used together: javascript - Array#map and parseInt
alert(["1","2","3"].map(parseInt));
As you can see it produces the unexpected output [1,NaN,NaN]. The reason this happens it because the map function calls parseInt with 3 arguments (item, index and array):
parseInt("1", 0, ["1","2","3"]) // 1
parseInt("2", 1, ["1","2","3"]) // NaN
parseInt("3", 2, ["1","2","3"]) // NaN
However, the parseInt function takes 2 arguments (string and radix):
First case, radix is 0 which is false. Hence default radix 10 is taken, resulting in 1.
Second case, radix is 1. There is no base 1 numeral system. Hence we get NaN.
Third case, radix is 2 which is valid. However there's no 3 in base 2. Hence we get NaN.
As you see, superfluous arguments can cause a lot of problems which are difficult to debug.
But, there is an alternative
So these extra arguments are helpful but they can cause a lot of problems. Fortunately, there is an easy solution to this problem.
In Haskell if you want to map over a list of values and the indices of each value then you use do it as follows:
map f (zip list [0..])
list :: [Foo]
[0..] :: [Int]
zip list [0..] :: [(Foo, Int)]
f :: (Foo, Int) -> Bar
map f (zip list [0..]) :: [Bar]
You could do the same thing in JavaScript as follows:
function Maybe() {}
var Nothing = new Maybe;
Just.prototype = new Maybe;
function Just(a) {
this.fromJust = a;
}
function iterator(f, xs) {
var index = 0, length = xs.length;
return function () {
if (index < length) {
var x = xs[index];
var a = f(x, index++, xs);
return new Just(a);
} else return Nothing;
};
}
We use a different map function:
function map(f, a) {
var b = [];
if (typeof a === "function") { // iterator
for (var x = a(); x !== Nothing; x = a()) {
var y = f(x.fromJust);
b.push(y);
}
} else { // array
for (var i = 0, l = a.length; i < l; i++) {
var y = f(a[i]);
b.push(y);
}
}
return x;
}
Finally:
function decorateIndices(array) {
return iterator(function (item, index, array) {
return [item, index];
}, array);
}
var xs = [1,2,3];
var ys = map(function (a) {
var item = a[0];
var index = a[1];
return item + index;
}, decorateIndices(xs));
alert(ys); // 1,3,5
Similarly you can create decorateArray and decorateIndicesArray functions:
function decorateArray(array) {
return iterator(function (item, index, array) {
return [item, array];
}, array);
}
function decorateIndicesArray(array) {
return iterator(function (item, index, array) {
return [item, index, array];
}, array);
}
Currently in Ramda you have two separate functions map and map.idx. The above solution allows you to replace map.idx with idx such that:
var idx = decorateIndices;
var arr = decorateArray;
var idxArr = decorateIndicesArray;
map.idx(f, list) === map(f, idx(list))
This will allow you to get rid of a whole bunch of .idx functions, and variants.
To curry or not to curry
There is still one small problem to solve. This looks ugly:
var ys = map(function (a) {
var item = a[0];
var index = a[1];
return item + index;
}, decorateIndices(xs));
It would be nicer to be able to write it like this instead:
var ys = map(function (item, index) {
return item + index;
}, decorateIndices(xs));
However we removed superfluous arguments because they caused problems. Why should we add them back in? Two reasons:
It looks cleaner.
Sometimes you have a function written by somebody else which expects these extra arguments.
In Haskell you can use the uncurry function to solve this problem:
map (uncurry f) (zip list [0..])
list :: [Foo]
[0..] :: [Int]
zip list [0..] :: [(Foo, Int)]
f :: Foo -> Int -> Bar
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f :: (Foo, Int) -> Bar
map (uncurry f) (zip list [0..]) :: [Bar]
In JavaScript the uncurry function is simply apply. It is implemented as follows:
function uncurry(f, context) {
if (arguments.length < 2) context = null;
return function (args) {
return f.apply(context, args);
};
}
Using uncurry we can write the above example as:
var ys = map(uncurry(function (item, index) {
return item + index;
}), decorateIndices(xs));
This code is awesome because:
Each function does only one job. Functions can be combined to do more complex work.
Everything is explicit, which is a good thing according to the Zen of Python.
There's no redundancy. There's only one map function, etc.
So I really hope this answer helps.

Categories