JavaScript curry / schönfinkeln - javascript

Lets say I have the following functions declared
function curry(fn) {
var args = [];
// push everything but function itself into args
for (var i = 1; i < arguments.length; ++i) {
args.push(arguments[i]);
}
return function() {
var args2 = [];
for (var i = 0; i < arguments.length; i++) {
args2.push(arguments[i]);
}
var argstotal = args.concat(args2);
return fn.apply(argstotal);
};
}
function add(a,b){return a + b};
What im trying to do is obvious, I want to curry the add function which works great itself
var addCurry = curry(add);
addCurry(10, 20, 12314) // should deliver 30, gives NaN
Somehow it returns NaN and I dont know what Im doing wrong... Anybody got an idea?

You have an off-by-one error here: return fn.apply(argstotal)
The first argument to Function.prototype.apply is a scope, the value of this within the invocation, not the first argument. If you were to print out the arguments from within your curried function, you would see:
function curry(fn) {
var args = [];
// push everything but function itself into args
for (var i = 1; i < arguments.length; ++i) {
args.push(arguments[i]);
}
return function() {
var args2 = [];
for (var i = 0; i < arguments.length; i++) {
args2.push(arguments[i]);
}
var argstotal = args.concat(args2);
return fn.apply(argstotal);
};
}
function add(a, b) {
console.log('debug', this, arguments);
return a + b;
}
var cadd = curry(add, 1);
cadd(2);
You can very easily fix this in two ways:
Pass this through to the curried function
Ignore this and do not set it for curried functions
The first option is probably better, as it will surprise developers less. You can implement that using:
function curry(fn) {
var args = [];
// push everything but function itself into args
for (var i = 1; i < arguments.length; ++i) {
args.push(arguments[i]);
}
return function() {
var args2 = [];
for (var i = 0; i < arguments.length; i++) {
args2.push(arguments[i]);
}
var argstotal = args.concat(args2);
return fn.apply(this, argstotal); // <-- only change
};
}
function add(a, b) {
console.log('debug', /* this, */ arguments);
return a + b;
}
var cadd = curry(add, 1);
cadd(2);
The second option can be implemented with fn.apply(null, argstotal).

Related

javascript array value is changing after it has been pushed

I am trying to debug the code below.
It is supposed to create a 2d-array, with all of the permutations of the input string.
It starts off great, and the initial string is pushed to the array, but after I run the reverse function in step 4, the value in strArr changes from having a length of 3 to a length of 2. basically like it is skipping the concat in the reverse function, but when I ran it in the debugger, z has a length of 3 after the concat, but then when the function returns it, the length becomes 2 again.
any help would be appreciated.
function permAlone(str) {
var perms = [];
var totalPerms = factorial(str.length);
var strCodes = converter(str);
var strArr = [];
strArr.push(strCodes);
// overall loop
for (var X = 0; X < totalPerms; X++) {
//step 1
var largestI = -1;
for (var i = 0; i < strCodes.length - 1; i++) {
if (strCodes[i] < strCodes[i + 1]) {
largestI = i;
}
}
//if none found break loop
if (largestI == -1) {
break;
}
//step 2
var largestJ = -1;
for (var j = 0; j < strCodes.length; j++) {
if (strCodes[largestI] < strCodes[j]) {
largestJ = j;
}
}
//step 3
swap(strCodes, largestI, largestJ);
//step 4
strCodes = reverse(strCodes, largestI);
//step 5 push to array
strArr.push(strCodes);
}
console.log(strArr);
return strArr;
}
function factorial(x) {
for (var i = x - 1; i > 0; i--) {
x *= i;
}
return x;
}
function converter(x) {
var temp = [];
for (var i = 0; i < x.length; i++) {
temp.push(x.charCodeAt(i));
}
return temp;
}
function swap(a, i, j) {
var temp = a[i];
a[i] = a[j];
a[j] = temp;
}
function reverse(z, a) {
var endArr = z.splice(a+1);
endArr.reverse();
z = z.concat(endArr);
return z;
}
debugger;
permAlone('abc');
The reverse function returns a new array and does not manipulate the existing. You need to change your code to the following:
endArr = endArr.reverse();
It looks like it was an issue with having a shallow copy of the array.
I added z = z.slice(); to the reverse function and it fixed the issue.

Pass variable from array into a function

I have this function:
function getTotal () {
var args = Array.prototype.slice.call(arguments);
var total = 0;
for (var i = 0; i < args.length; i++) {
total += args[i];
}
return total;
}
Lets say that I have an array that is filled with numbers and I do not know the length of it:
var numArray = [ ..., ... ];
How can I call the function getTotal by passing in every element in the numArray as a parameter?
In ES6 you can do this:
getTotal(...numArray);
It called Spread syntax. For more info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
You can call the function using Function.prototype.apply. It will pass the Array as arguments to your function.
function getTotal () {
var args = Array.prototype.slice.call(arguments);
var total = 0;
for (var i = 0; i < args.length; i++) {
total += args[i];
}
return total;
}
var numArray = [1,2,3,4];
console.log( getTotal.apply( null, numArray ) );
Keep in mind you have a typo in your for loop. Should be args.length, instead of arg.length.
Please try this. Hope it helps
var sum = [1, 2, 3, 4, 5].reduce(add, 0);
function add(a, b) {
return a + b;
}
If you use the spread operator, you may also want to ES6ify your code:
function getTotal(...vals){
return vals.reduce((a,b)=>a+b,0);
}
getTotal(...[1,2,4]);
Why not just do this:
function getTotal (args) {
var total = 0;
for (var i = 0; i < args.length; i++) {
total += args[i];
}
return total;
}
var numArray = [1,2,3];
console.log(getTotal(numArray)); //6

Cannot set property 'XY' of undefined

I have following code:
var favourites = JSON.parse(localStorage.getItem("favourites"));
Service.all().then(function (multiple) {
var array = [];
for (var i = 0; i < multiple.length; i++) {
for (var j = 0; j < favourites.length; j++) {
if (favourites[j].checked === true) {
if (multiple[i].Name === favourites[j].name) {
Service.getAllBySomething(multiple[i].Id).then(function (resources) {
var arrayOfSomething = [];
for (var k = 0; k < resources.length; k++) {
arrayOfSomething.push(resources[k].ResourceCategoryId);
}
arrayOfSomething = arrayOfSomething .filter(function (elem, pos, arr) {
return arr.indexOf(elem) == pos;
});
multiple[i].existingProperty= arrayOfSomething;
});
array.push(multiple[i]);
}
}
}
}
$scope.vendors = array;
});
My problem is that it says everytime 'Cannot set property existingProperty of undefined'. And I don't know why multiple[i] should be undefined at this line:
multiple[i].existingProperty= arrayOfSomething;
The property exists, I am sure. And it is defined, it is an empty array. And this empty array I want to replace with my made array in the loops.
Where is the fault? How I can fill the existingProperty with my array?
is Service.getAllBySomething asynchronous by any chance? Because in that case, by the time the callback function runs, i (captured in a closure only) has been moved to the end of the array.
Use an additional closure to capture the value of i at the time of dispatching the async call, like this:
Service.getAllBySomething(multiple[i].Id).then(function(i){
return function (resources) {
var arrayOfSomething = [];
for (var k = 0; k < resources.length; k++) {
arrayOfSomething.push(resources[k].ResourceCategoryId);
}
arrayOfSomething = arrayOfSomething .filter(function (elem, pos, arr) {
return arr.indexOf(elem) == pos;
});
multiple[i].existingProperty= arrayOfSomething;
};
}() );
Note the new function receiving i as a param and returning the function you previously used. I'm invoking immediately (an IIFE) to return the function to pass as callback. Inside that function the value of i will be the same as when you dispatch this async call, as it was copied when used as a parameter.

How to create a parameterized infix macro in sweet.js

In the bluebird wiki article about JavaScript optimization killers, the author mentions that passing the arguments keyword to any function (except apply) will cause the parent function to not be optimizable. I would like to create a sweet.js macro that allows me to write standard idiomatic JavaScript but will take care of the optimization killer.
Ideally, I would like a macro that would take the following function:
function foo() {
var args = [].slice.call(arguments);
return args;
}
And output something like this:
function foo() {
var args = [];
for(var i, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
return args;
}
I am having trouble with getting the sweet.js macro syntax correct, however. This is what I have so far:
example.sjs
let arguments = macro {
rule infix {
[].slice.call |
} => {
[];
for(var i = 0, len = arguments.length; i < len; i++) {
args.push(arguments[i])
}
}
}
function toArray() {
var args = [].slice.call arguments
return args;
}
Which outputs the following:
function toArray() {
var args$2 = [];
for (var i = 0, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
return args$2;
}
I tried making my macro have parenthesis around the arguments keyword and also include the var declaration, but without any success. I tried something like this:
invalid macro
let arguments = macro {
rule infix {
var $var = [].slice.call ( | )
} => {
var $var = [];
for(var i = 0, len = arguments.length; i < len; i++) {
args.push(arguments[i])
}
}
}
This produces the following error:
SyntaxError: [syntaxCase] Infix macros require a `|` separator
414:
^
Right, so there are a couple of ways to do this. Putting arguments
inside of parens doesn't work because infix macros can't match outside
of enclosing delimiters so when the arguments macro gets invoked it
sees zero tokens before or after it (the error should be clearer).
Your other solution is running into hygiene problems since the
arguments macro needs access to the args identifier but infix
macros are not allowed to match before the equals sign when it's in a
var statement so it can't actually match the args identifier.
So couple of solutions. The easiest is to just do something like what
the bluebird wiki suggested:
macro argify {
rule { ( $arg ) } => {
var $arg;
for (var i, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
}
}
function foo() {
argify(args)
return args;
}
You could also go the unhygienic route (not really recommended but
arguments is already kinda unhygienic so…):
let function = macro {
case {$mname $name ( $parens ...) { $body ... } } => {
letstx $args = [makeIdent("args", #{$mname})];
return #{
function $name ( $parens ...) {
var $args = [];
for (var i, len = arguments.length; i < len; i++) {
$args.push(arguments[i]);
}
$body ...
}
}
}
}
function foo() {
return args;
}
Edit:
I just thought of another solution that would allow you to keep your current syntax by overriding var:
let var = macro {
rule { $args = [].slice.call(arguments) } => {
var $args = [];
for(var i, len = arguments.length; i < len; i++) {
$args.push(arguments[i]);
}
}
rule { $rest ... } => { var $rest ... }
}
function foo() {
var args = [].slice.call(arguments);
}
This isn't quite the same result, since it has a function wrapper (though it's invoked with apply), but it doesn't require you to override var and can be used in any expression position.
macro copy_args {
rule {} => {
function() {
var len = arguments.length;
var args = Array(len);
for (var i = 0; i < len; i++) {
args[i] = arguments[i];
}
return args;
}.apply(this, arguments)
}
}
let slice = macro {
rule infix { []. | .call(arguments) } => { copy_args }
rule infix { []. | .apply(arguments) } => { copy_args }
rule infix { Array.prototype. | .call(arguments) } => { copy_args }
rule infix { Array.prototype. | .apply(arguments) } => { copy_args }
rule { } => { slice }
}
function go() {
var args = Array.prototype.slice.call(arguments);
return args;
}
expands to
function go() {
var args = function () {
var len = arguments.length;
var args$2 = Array(len);
for (var i = 0; i < len; i++) {
args$2[i] = arguments[i];
}
return args$2;
}.apply(this, arguments);
return args;
}
Don't know if that would kill optimization though...

How do I properly pass arguments more than once in a javascript object literal?

I have an object literal similar to this:
var test = {
myFunc1: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc1: ' + arguments[i]);
}
},
myFunc2: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc2: ' + arguments[i]);
}
this.myFunc1.apply(this.myFunc2, arguments);
},
myFunc3: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc3: ' + arguments[i]);
}
this.myFunc2.apply(this.myFunc3, arguments);
}
}
When I call "myFunc3" I want it to call "MyFunc2" with all the arguments passed to "myFunc3". This is working as expected.
When I call "myFunc3" I want it to call "myFunc2" with all passed arguments, and I want myFunc1 to be called with all passed arguments that were originally passed to "myFunc3".
test.myFunc3('a', 'b');
Currently when I call this, I get 4 alert boxes, 2 for myFunc3 and 2 for myFunc2. Then I get an error:
Uncaught TypeError: Cannot call method 'apply' of undefined
If I need to pass a set of arguments more than one level deep in an object how can I do this? Is there a better more correct way to do this?
Here is a jsFiddle to demonstrate.
http://jsfiddle.net/taggedzi/fmPAQ/2/
Thanks in advance. Pure javascript only, no jquery please. Needs to work cross browser and platform.
That's because the this in myFunc2 is not anymore test but this.myFunc3. This was provided by your myFunc3 when calling this.myFunc2.apply(this.myFunc3, arguments);.
To visualize, your myFunc2 is doing:
(this.myFunc3).myFunc1(...);
//instead of
(this).myFunc1(...);
You should pass in only this so you are using the same this for the other functions.
this.myFunc2.apply(this, arguments);
Try this: it works. You should use this instead of this.FuncX
var test = {
myFunc1: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc1: ' + arguments[i]);
}
},
myFunc2: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc2: ' + arguments[i]);
}
this.myFunc1.apply(this, arguments);
},
myFunc3: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc3: ' + arguments[i]);
}
this.myFunc2.apply(this, arguments);
}
};
test.myFunc3('a', 'b');

Categories