I want to abstract the function that gets passed into my arrays' reduce() functions to make the function a generic 'greatest of the Array reducer'. To achieve this, I want to then pass in different specific functions in the reduce() parameters to be able to specify the criteria for comparison. In my example, these are the lines
return players.reduce(topPlayerReducer, players[0], getThreePointerPercentage);
and
return players.reduce(topPlayerReducer, players[0], getReboundNumber);
where topPlayerReducer is a generic function passed to reduce() that finds the biggest item in an array based on some criteria. My criteria are the 3rd arguments. How can I incorporate my specific comparison functions (getThreePointerPercentage and getReboundNumber) so that I keep this level of abstraction? Right now, I'm getting the error that fn is not a function in topPlayerReducer. I'm not surprised by this, because my other functions aren't within the scope. I've also tried doing
var reboundReducer = topPlayerReducer.bind(getReboundNumber);
return gameInfo.players.reduce(reboundReducer, players[0]);}
but I've gotten the same error.
I realize I can achieve a result with making two different functions for reduce(), but that doesn't satisfy me. I want to know if there is a way to do it differently.
function getGuardWithMostThreePointers(gameInfo){
return players.reduce(topPlayerReducer, players[0], getThreePointerPercentage);
}
function getPlayerWithMostRebounds(gameInfo){
return players.reduce(topPlayerReducer, players[0], getReboundNumber);
}
function topPlayerReducer(topPlayer, currentPlayer, fn){
if (fn(currentPlayer) > fn(topPlayer)){
return currentPlayer;
} else {
return topRebounder;
}
}
function getReboundNumber(player){
return parseInt(player.rebounds_offensive) + parseInt(player.rebounds_defensive);
}
function getThreePointerPercentage(player){
return parseInt(player.three_pointers_made) / parseInt(player.three_pointers_attempted);
}
I would do it like so:
Change the implementation of topPlayerReducer so that it returns a function which compares two players, rather than comparing the players itself:
function topPlayerReducer(fn){
return function(topPlayer, currentPlayer) {
if (fn(currentPlayer) > fn(topPlayer)){
return currentPlayer;
} else {
return topPlayer;
}
}
}
Then you can call reduce like so:
return pointGuards.reduce(topPlayerReducer(getThreePointerPercentage), pointGuards[0]);
or
return gameInfo.players.reduce(topPlayerReducer(getReboundNumber), gameInfo.players[0]);
This way you can pass in a custom function with each different call you make to reduce, you just 'wrap it up' in topPlayerReducer first. I think this is what you were trying to achieve with bind.
FYI: I think what you were looking for from bind is something called partial application, where you take a function with multiple arguments, supply some but not all of the arguments, and get back a function which expects the remaining arguments.
You can do this with bind, but you have to remember:
that bind takes an extra argument which is bound to this within the function,
that the arguments you are 'pre-loading' will fill in from the left.
So your attempt to use bind would have worked if you'd made these changes:
// Make fn the leftmost parameter
function topPlayerReducer(fn, topPlayer, currentPlayer){
if (fn(currentPlayer) > fn(topPlayer)){
return currentPlayer;
} else {
return topRebounder;
}
}
// Add an extra 'null' argument to be bound to the `this` variable
return players.reduce(topPlayerReducer.bind(null, getReboundNumber), players[0])
For my money, the bind version just adds clutter in this situation. bind can be useful when you have functions which make use of this, though, and you need a way to change its value.
Related
I created a class that supports chaining by making use of return this;, and
now I need to make the current method tell what methods can be chained. Example:
class MyClass {
constructor(path) {
this.path = path;
}
method1() {
// some code here...
// i must make something here to only allow channing if it's method
// blabliblu
return this;
}
blabliblu() {
// some code here...
// same thing here, if the channing is with the method ar_2, it's ok.
return this;
}
ar_2() {
// And so on...
return this;
}
}
So, i can do: method1().blabliblu(), but i can't do method1().ar_2(). Is there lib to help me achieve this?
What you have asked for is not possible in Javascript. When you do:
return this;
in your methods, you are returning the same object. Any public method can be called on the object that is returned and there is no way to specify that because the object was returned from a method it should somehow not allow some methods to be called.
So, using the code in your example where you return this in .method1(), there's no difference between this:
obj.method1();
obj.ar_2();
and this:
obj.method1().ar_2();
That's because the chained version is essentially this internal to the JS interpreter:
let temp = obj.method1();
temp.ar_2();
So, if temp is the same as obj which it is when you return this from method1(), then obj.method1().ar_2(); is just the same as obj.method1(); obj.ar_2(); (with a little less typing). Thus, you can't prevent the calling of .ar_2().
Both just call .method1() and then .ar_2() on the same object. So you can't prevent one scheme, but allow the other. ar_2 is either a public method or it isn't. You can't have it callable in one place and not callable in another on the same object.
Now, you could make obj.method1() return a different object. If you did that, then that different object could have different methods on it and could be an object that does not have a .ar_2() method.
When you chain array methods like this:
let result = [1,2,3].map(...).filter(...);
Each step in that chain is returning a different object (they are not doing a return this, but are creating a new object and returning it. In this specific Array example, these are returning different objects, but of the same type, but you could return different objects of different types. For example:
let result = ["a","b","c"].join("").toUpperCase();
Here, .join() is an Array method, but returns a string object which you can then only call string methods on. So, you could do something like that where you return a different type of object.
I am looking for a way to declare a function without declaring one or more parameters. This would be used in cases where you're implementing a function where you don't care what the value of (for instance) the first argument is.
For example, given this function:
function foo(this_arg_is_unused, important_arg) {
doSomethingWith(important_arg);
}
Is there some way to declare it more like this?
function foo( , important_arg) {
doSomethingWith(important_arg);
}
I realize I could easily do this:
function foo() {
doSomethingWith(arguments[1]);
}
However that starts becoming less readable and it would be more difficult to (for instance) use fn.js to curry the arguments.
I'm currently thinking perhaps I could just use a visual mnemonic to indicate the argument is not used:
function foo(ø, important_arg) {
doSomethingWith(important_arg);
}
However this may also have readability issues.
Real world example:
The callback function used by jQuery's .each() takes two arguments, the first is either the index or property name in the array/object and the second is the value. To manipulate some div elements you might do something like this:
$($('div').each(function(index, value) {
console.log(value); // equivalent to doSomething(this)
}));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Nothing here</div>
Using an arrow function (where using arguments[] is not possible)
$($('div').each((ø, value) => console.log(value)));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Nothing here</div>
It there a way to leave a function argument undeclared?
No. Just omitting it is a syntax error.
Perhaps I could just use a visual mnemonic to indicate the argument is not used
Yes, that's the right way. The idiomatic name for unused parameters is the underscore _, known from other languages where it has a special meaning (and can even be used multiple times):
function foo(_, important_arg) {
Real world example: the callback function used by jQuery's .each()
Yes, jQuery has messed up the arguments order of the callback, usually the value goes first. In jQuery, the value goes in the zeroeth argument - the this value, so idiomatic jQuery is
$('div').each(function() {
console.log(this);
});
If you cannot use this for some reason, you will have to use the second parameter; there's no way around this.
You can't leave empty spaces in function arguments. A placeholder argument works well.
If you don't want to do that, you could write a higher-order function that wraps a function to expect a first, throwaway argument:
function withExtraArg(f, thisValue = null) {
return (x, ...args) => f.call(thisValue, ...args);
}
And write your function taking a natural number of arguments:
function withExtraArg(f, thisValue = null) {
return (x, ...args) => f.call(thisValue, ...args);
}
// your function
function log (value) {
console.log(value)
}
$($('div').each(withExtraArg(log)));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Nothing here</div>
I wrote a simple function to sort objects not expecting it to work but it does:
function sortObjs(objArr,field) {
objArr.sort(
function(a,b) {
if( isNaN(a[field]) ) {
return a[field].localeCompare(b[field]);
} else {
return parseFloat(a[field]) - parseFloat(b[field])
}
}
);
return objArr;
}
When I call this function I get my sorted objects without issue. But I was not expecting it to work because I thought the first two return statements would exit the function before it got to the last statement: return objArr.
You have a nested function. The first two returns will exit the inner function, while the last one will exit the outer one.
EDIT:
You can think of function returns as "replacing" the function with the returned value. For example:
var i = getSum(1, 3);
function getSum(a, b) {
return a + b;
}
The function getSum returns the sum of a and b. The line var i = getSum(1, 3) will execute the lines of code contained in the function with a = 1 and b = 3. The value that is returned "replaces" the function call. So now the line of code looks like var i = 4;. Although this is not exactly how it works, it's a good way to conceptualize it. This is a special case because you aren't actually running the inner method here, you're passing it as a variable.
Let me know if you have any more questions!
To understand why the inner function's return statements would not have an effect on the return statement in the outer scope, you need to understand how the Array.prototype.sort() function works.
The function arr.sort([compareFunction]), takes a function as a parameter.
compareFunction
Optional. Specifies a function that defines the sort order.
If omitted, the array is sorted according to each character's Unicode code point value,
according to the string conversion of each element.
The logic you write inside the compareFunction gets executed when
the java script engine tries to compare two elements during its
comparison operation.
Hence for each comparison that it makes, the function would return a
value, based on which the elements would be ordered.
This implies that the compareFunction that we pass on as a parameter
would be used to just obtain a value based on which two elements can
be compared and not exit the sort operation, leave alone exiting the
outer function.
Having said this, the code return objArr;, takes no effect, since the array would be sorted in place.
I'm familiar with the way call(), which you can pass a variable number of arguments that will be loaded into a function's parameters when called. I'm trying to do something related where I recurse through nested set objects in RaphaelJS with forEach (analogous to jQuery's each), determine whether the child element is another set, and apply a function with a variable number of arguments if not. I want to make it generic so that I can apply any function, but make the functions that I pass have simple parameter constructors without having to access the arguments property of the function.
function recursiveFncApply(set, fnc, args) {
set.forEach(function(item) {
if (item.type == 'set') {
recurseApplyFncToSets(item, fnc, args);
} else {
fnc(item, args);
}
});
}
function translateOperation(element, operation, x, y)
// do stuff to element with operation, x, and y args without accessing
// accessing arguments property
}
recursiveFncApply(passedSet, translateOperation, [arg1, [arg2, ...]]);
I want to do this so that I can use multiple functions without having to repeat myself with code that determines arguments and properly assigns them before usage. I'm not sure whether there's some kind of functionality or language utility that I'm missing that would enable me to do this, or somehow to programmatically "construct" a function call from the remaining arguments passed to recursiveFncApply. Is this possible in JavaScript?
Clarification: I want to pass a variable number of arguments to my recursive function that will be passed to any function that I want to be applied to the contents of the sets my recursive function is working on. So I want to be able to make recursiveFncApply work generically with any function while still using an argument structure that works like a function being executed via call().
Say I have another function in addition to translateOperation:
function anotherFunction(element, differentArg) {
// do something with one argument
}
Ideally I could then use my recursiveFncApply in this way:
recursiveFncApply(passedSet, translateOperation, operation, x, y);
recursiveFncApply(passedSet, anotherFunction, singleArg);
As well as this way:
recursiveFncApply(passedSet, anotherFunction, singleArg);
I believe that this is similar to how call() works in that I could do:
anotherFunction.call(this, element, differentArg);
.. without having to change the structure of anotherFunction to sort out the arguments property, or pass an object/array.
It turns out that Felix King had the right idea/was the closest. I found a direct answer to my question as soon as I realized what I was actually trying to do, which is pass forward arguments from function to function (found the answer here). So I got this to work with this code:
function recursiveSetFncApply(set, fnc/*, variable */) {
var me = this;
var parentArgs = arguments;
set.forEach(function(element) {
if (element.type == 'set') {
parentArgs[0] = element;
me._recursiveSetFncApply.apply(me, parentArgs);
} else {
// Generate args from optional arguments and pass forward; put element in args at front
var args = Array.prototype.slice.call(parentArgs, 2);
args.unshift(element);
fnc.apply(element, args);
}
});
}
I make a reference to arguments with parentArgs because I need to update the set property in arguments before I pass it forward to the next recursive loop, otherwise I hit an infinite loop because it hasn't updated at all because it's using the original arguments set. I was under the impression that apply() will not actually pass forward arguments, but simply pop an array into the new function that you have to access by index--this isn't the case. When I used apply() on translateElementOperation, I had all the arguments I needed in their exact places. Here's the updated function I ran through the recursive apply:
function translateElementOperation(element, operation, x, y) {
var currentPath = element.attr('path');
translatedPath = Raphael.transformPath(currentPath, [operation, x, y]);
element.attr('path', translatedPath);
}
Thanks for the help, everyone!
Use .apply instead of .call
functionName.apply(element, [any, number, of, variables, ...]);
// instead of this
functionName.apply(element, set, of, variables, ...);
This is more useful like so:
var fnVars = [];// fill this anyway you want.
functionName.apply(element, fnVars);
I'm learning lots of javascript these days, and one of the things I'm not quite understanding is passing functions as parameters to other functions. I get the concept of doing such things, but I myself can't come up with any situations where this would be ideal.
My question is:
When do you want to have your javascript functions take another function as a parameter? Why not just assign a variable to that function's return value and pass that variable to the function like so:
// Why not do this
var foo = doStuff(params);
callerFunction(foo);
//instead of this
callerFunction(doStuff);
I'm confused as to why I would ever choose to do things as in my second example.
Why would you do this? What are some use cases?
Here's yet another example. Does some formatting operations on an array:
function pctFormatter(num) {
return num + '%';
}
function centsFormatter(num) {
return num + '.00';
}
function formatThisArray(array, formatter) {
var output = [];
for(var i = 0; i < array.length; i++) {
output.push( formatter(array[i]) );
}
return output;
}
formatThisArray([1,2,3], pctFormatter);// returns ['1%', '2%', '3%']
formatThisArray([1,2,3], centsFormatter);// returns ['1.00', '2.00', '3.00']
Handlers/listeners are a good example.
More generally, you can pass a function f as a parameter to function g when you don't know yet if g will need to call f, how many times it will need to call it, and/or with which parameters.
Examples:
sort algorithms: comparison function
regular expressions: replace function
callbacks (e.g. event handlers)
You'd do it when you don't have the params to pass, but the callerFunction() does.
A callback to an AJAX request is one use case.
function myCallback(response) {
// do something with the response
}
myAJAX('http://example.com/foo.json', myCallback)
This lets myAJAX to the work of making the request, and waiting for the response. Then it invokes myCallback and passes it the response when that response finally arrives.
// Why not do this
var foo = doStuff(params);
callerFunction(foo);
//instead of this
callerFunction(doStuff);
First example will run the function doStuff with params and the assign the result to foo. callerFunction will be called with parameter foo (which is now a result of dooStuff);
Second example will call callerFunction and pass doStuff as a parameter. The callerFunction might or might not call the doStuff.
Well, sometimes you don't know who the caller of a function will be until it's called - this precludes passing pre-calculated values.
A couple of examples that spring to mind are:
(a) setTimeout or setInterval - you want to call a specific function after a specified period, either one-shot, or repeatedly. If the function called returned a value that had a dependance on time, there are instances where you couldn't possibly pre-calculate the value - it needs to be done at the scheduled time. So, we tell the functions which of our own functions to call at the specified time.
(b) when loading (or at least attepmpting to) various resources. We can give the element a function that is to be executed when loading is successful, and another when it fails. You don't actually know when the effort to load a resource has finished until either of these two (user-supplied) functions are called. In the case of many resources, this is where you increment the counters that maintain the number of successful/failed load attempts.
(c) the NodeList returned by calls to getElementsByClass or getElementsByTagName. It's not an actual (javascript native) Array object. As such, you can't call the forEach method on it, like you can with an array. To get around this, I use the following helper function:
// getElementsByTagName, getElementsByClass - both return a NodeList
// it is accessed in the same way as an array - with the [] operators, but it's
// not an array object - this is a function that allows us to still iterate through it
// in much the same way.
function forEachNode(nodeList, func)
{
var i, n = nodeList.length;
for (i=0; i<n; i++)
{
func(nodeList[i], i, nodeList);
}
}
This allows me to get a list of nodes and then call some user-defined function on each of them. In use, it looks like this:
var allAnchors = document.getElementsByTagName('a');
forEachNode(allAnchors, showNodeTextVal);
function showNodeTextVal(curElem, curIndex, origList)
{
alert(curElem.innerText);
}
Or more simply:
var allAnchors = document.getElementsByTagName('a');
forEachNode(allAnchors, function(curElem){alert(curElem.innerText);} );
This is a much clearer, less error-prone situation than it would be if we didn't use this helper function. To achieve the same functionality, we'd need to code the following:
var nodeList = document.getElementsByTagName('a');
var i, n = nodeList.length;
for (i=0; i<n; i++)
{
alert(nodeList[i].innerText);
}
Most common case is handlers in JQuery:
function clickHandler(e){
// handle click on e.Target
}
$("#button").click(clickHandler);
$(function(){
// do ready state initialization
});
callerFunction(doStuff);
with this code you give a "pointer" of the function doStuff to the function callerFunction
you can use it like this:
function callerFunction(doStuff) {
var x = doStuff(...);
...;
}
you can so use the function in the function and not only the return value of doStuff.
greetings!
When do you want to have your javascript functions take another
function as a parameter?
It's useful for callbacks for example:
function add( a, b, callback ) {
callback( a, b );
return a + b;
}
function added( a, b ) {
alert('You just added two numbers: '+ a +' and '+ b);
}
alert( add( 1, 2, added ); // Will alert the message and then the result.
This a very simple example but it's very useful with asynchronous functions so you can run code after it has finished without interrupting the script.
You need to pass functions themselves, not return values, when you want to have your code really deal with functions as functions - code to execute. Consider this pseudo-code example:
function saveToLocalStorage(data) {...//saves to local storage}
function saveToServer(data) {...//saves via AJAX to server}
function saveToAmazonS3(data) {.../saves to Amazon S3 }
function multiSave(data, saverFunctions) {
saverFunctions.forEach(function (saverFunction) {
saverFunction(data);
});
}
multiSave({user: "tim"}, [saveToLocalStorage, saveToServer, saveToAmazonS3]);
In this case, I want the actual functions themselves to be passed around and for other code to later invoke them. When we do this, a function such as multiSave is called a higher-order function because it deals with other functions directly. Because of the way multiSave works, I can easily put some checkboxes in the UI next to local/server/S3 and allow the user to choose where their data goes in a way that would be less elegant if I was unable to pass functions around as arguments.
When you're passing a function as an argument, that argument is not the return value of that function, but it's the function itself, you can call it as much as you like, with any argument you like, or you can assign it to an event. You say you want some practical use cases, here's a short list of very common situations, all requiring a function to be passed as an argument.
Let's take a look at your average jQuery code, and count the number of times where a function is passed as an argument:
$(document).ready(function()//<-- 1
{
$('#foo').on('click',function()//2
{
});
$.each(something,function()//3
{});
//and so on
});
If you don't use jQuery, then try event delegation
document.body.addEventListener('click',function(e)
{
e = e || window.event
console.log('This function was passed as an argument to the addEventListener method');
},false);
Or even the simple Array.prototype.sort function (/method):
anArray.sort(function(a,b)
{
return (a > b ? 1 : -1);
});
Or in cases where you need to make an ajax call, instead of creating a new XMLHttpRequest object on the spot, you might want a single function that sets the xhr object up, and pass the url, data and onreadystatechange callback as arguments:
function makeXHR(url,data,callback)
{
try
{
var xhr = new XMLHttpRequest();
}
catch(e)
{
//etc...
}
xhr.onreadystatechange = callback;
}
makeXHR('some/url','foo=bar',function()
{
if (this.readyState === 4 && this.status === 200)
{
//do stuff
}
});
In all of these examples, I've created the functions in-line, of course referencing a function (by just passing its name) works just fine, too:
makeXHR('some/url','foo=bar',defaultXhrCallback);
These are just a few of thousands of use cases where you can/have to pass a function as an argument to another function