I know I can get the String representation of a function without calling it like this
function storeData(id, data) { console.log("Doing stuff..") };
storeData.toString(); //"function storeData(id, data) { console.log("Doing stuff..") }"
And I could in theory parse the resulting String to pull out the variable names. Any takers on writing that code? Is there an easier way? (I don't need to worry about minification)
try to use the following code:
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
var fnStr = func.toString().replace(STRIP_COMMENTS, '');
var result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
if (result === null)
result = [];
return result;
}
getParamNames(getParamNames) // returns ['func']
getParamNames(function (a, b, c, d) { }) // returns ['a','b','c','d']
getParamNames(function (a,/*b,c,*/d) { }) // returns ['a','d']
getParamNames(function () { }) // returns []
Related
I am creating a function memo return a function that, when called, will check if it has already computed the result for the given argument and return that value instead if possible.
Right now my key only contains the first argument passed in as my key to check if the function was already run.
const memo = function(func) {
const memoizedFunc = function () {
let result;
if (memoizedFunc.cache[arguments[0]] !== undefined) {
result = memoizedFunc.cache[arguments[0]];
} else {
result = func.apply(null, arguments);
memoizedFunc.cache[arguments[0]] = result;
}
return result;
}
memoizedFunc.cache = {};
return memoizedFunc;
};
So if we run this using the test function:
function memoAdd(num1, num2){
return num1 + num2;
}
memo(memoAdd(1, 2)));
memo(memoAdd(1, 3))); // -> since it's the first argument that we use as
// the key, it will still pull up 3 as the answer even if the answer should
// be four
Any idea how should I fix this so I'll give different results for different arguments?
Your test call will fail because it is not passing a callable to memo, but the results of the function.
If the idea is to avoid repeated computation and merely return the same result, you could do it like this:
var function_cache = {};
const memo1 = function(f, args) {
var sig = JSON.stringify(args);
if (f.cache == undefined) {
f.cache = {};
}
if (f.cache[sig] !== undefined) {
console.log('Value from Cache');
return f.cache[sig];
}
console.log('Value from Computation');
f.cache[sig] = f.apply(null, args);
return f.cache[sig];
}
function memoAdd(num1, num2){
return num1 + num2;
}
console.log(memo1(memoAdd, [1, 2]));
console.log(memo1(memoAdd, [1, 3]));
console.log(memo1(memoAdd, [1, 2]));
console.log(memo1(memoAdd, [1, 3]));
In this case, the hash cache is added to the function that is passed, and the arguments are converted to a string signature via JSON.stringify - which is used to look up the prior value and return it.
If your goal is to have a callable that does this so you don't need to wrap it in memo each time, you could tweak this:
const memo2 = function(f) {
if (f.cache == undefined) {
f.cache = {};
}
const newFunc = function() {
var sig = JSON.stringify(arguments);
if (f.cache[sig] !== undefined) {
console.log('Value from Cache');
return f.cache[sig];
}
console.log('Value from Computation');
f.cache[sig] = f.apply(null, arguments);
return f.cache[sig];
}
return newFunc;
}
function memoAdd(num1, num2){
return num1 + num2;
}
var myFunc = memo2(memoAdd);
console.log(myFunc(1,2));
console.log(myFunc(1,3));
console.log(myFunc(1,2));
console.log(myFunc(1,3));
In this case the passed in function is locked-in by the closure, and we still cache results in f.cache.
I have tried writing the below code to find sum of 'n' numbers using sum function. I am getting the correct response in output. But i am unable to return that using sum function, as i always have to return a function, which is required for curried effect.
Please help. Thanks in advance.
var output = 0,
chain;
function sum() {
var args = Array.prototype.slice.call(arguments);
output += args.reduce(function(a, b) {
return a + b;
});
sumCurried = sum.bind(output);
sumCurried.val = function() {
return output;
}
return sumCurried;
}
debugger;
document.getElementById('demo').innerHTML = sum(1, 2)(3)(4);
// document.getElementById('demo').innerHTML = sum(1)(3)(4);
<p id='demo'></p>
enter code here
You can add a stop condition to the curried function, for example - if the function is called without an argument return the output:
var output = 0,
chain;
function sum() {
var args = Array.prototype.slice.call(arguments);
if(args.length === 0) {
return output;
}
output += args.reduce(function(a, b) {
return a + b;
});
sumCurried = sum.bind(output);
return sumCurried;
}
console.log(sum(1, 2)(3)(4)());
<p id='demo'></p>
The returned curry function has a val property, which is a function that returns the current value:
var output = 0,
chain;
function sum() {
var args = Array.prototype.slice.call(arguments);
output += args.reduce(function(a, b) {
return a + b;
});
sumCurried = sum.bind(output);
sumCurried.val = function() {
return output;
}
return sumCurried;
}
console.log(sum(1, 2)(3)(4).val());
<p id='demo'></p>
Why would you use currying at all? However, here is a shorter version:
const sum = (...args) => {
const func = (...s)=> sum(...args,...s);
func.value = args.reduce((a,b)=>a+b,0);
return func;
};
//usable as
sum(1,2).value,
sum(1,1)(1).value,
sum(1,1)(1,1)(1,1).value
And you always need to end the currying chain. However, it can be shortified:
func.valueOf = ()=> args.reduce((a,b)=>a+b,0);
//( instead of func.value = ... )
So when called you can do:
+sum(1,2,3)
+sum(1)(1)(1)
I implemented an aggregation function but the only problem I have now is that I lost my key: value format e.g [{name:"Apples",val:8},{name:"Banana",val: 9}].
function agrregate(a){
var targetObj = {};
var result;
var b = JSON.parse(JSON.stringify(a));
var trees= b.length;
if(!trees){
trees = 0
}
for (var i = 0; i < trees; i++) {
if (!targetObj.hasOwnProperty(b[i].key)) {
targetObj[b[i].key] = 0;
}
targetObj[b[i].key] += b[i].val;
}
result = JSON.stringify(targetObj);
return result;
}
This is the result i get when agrregate function completes.
{"Apple":8,"Banana":9}
Instead of
{name:"Apple", val:8}, {name:"Banana", val:9}
Use a reducer to aggregate. You don't need to do stuff with JSON stringify/parse.
To get back to an array of objects, you use map and Object.keys
var test = [{name:"Apples",val:5},{name:"Banana",val: 9},{name:"Apples",val:3}]
var aggregate = function(arr) {
return arr.reduce(function(result, obj) { // Create one object (result)
result[obj.name] = (result[obj.name] || 0) + obj.val; // Add a new key/or increase
return result // Return the object
}, {});
};
var wrap = function(obj) {
return Object.keys(obj) // Create an array of keys
.map(function(key) {
return { // Specify the format
name: key,
val: obj[key]
};
});
};
console.log(aggregate(test));
console.log(wrap(aggregate(test)));
I am trying to write a function that takes functions as arguments (as many as it gets) and returns them. The function funcArg should return 'Called me'. I used Array.prototype.slice.call(arguments); to create an array but I don't know how to call die functions in that array. Any ideas? Thanks!!
var caller = function() {
return "Called ";
};
var adder = function() {
return " me";
};
var funcArgs = function() {
var myArray = Array.prototype.slice.call(arguments);
}
funcArgs(caller);
funcArgs(calleradder);
You can do this using reduce.
var funcArgs = function() {
var functions = Array.prototype.slice.call(arguments);
return functions.reduce(function(total, f) {
return total + f();
}, '');
};
The way this works if you start off with an array of functions. We then go through each function one at a time. We then call that function and append it to the result of the previous function. Breaking this down into simpler code would look like this:
var funcArgs = function() {
var functions = [caller, adder];
var result = '';
result += functions[0](); // caller();
result += functions[1](); // adder();
return result;
};
If you have an array of functions you can loop over them with forEach.
var caller = function() {
return "Called "
}
var adder = function() {
return " me"
}
var funcArgs = function() {
var myArray = Array.prototype.slice.call(arguments);
myArray.forEach(function (fn) {
console.log(fn())
})
}
funcArgs(caller, adder); // "Called me"
If you want to actually return the values, rather than just console.log them, you can use reduce to return the strings concatenated (or whatever else)
var funcArgs = function() {
var myArray = Array.prototype.slice.call(arguments);
return myArray.reduce(function (acc, fn) {
return acc + fn()
}, '')
}
How to negate a boolean function?
Such that using something like:
_.filter = function(collection, test) {
var tmp = []
_.each(collection, function(value){
if (test(value)) {
tmp.push(value);
}
})
return tmp
};
var bag = [1,2,3];
var evens = function(v) { return v % 2 === 0};
This is wrong:
// So that it returns the opposite of evens
var result = _.filter(bag, !evens);
result:
[1,3]
Underscore has a .negate() API for this:
_.filter(bag, _.negate(evens));
You could of course stash that as its own predicate:
var odds = _.negate(evens);
then
_.filter(bag, odds);
Try making a function that returns a function:
function negate(other) {
return function(v) {return !other(v)};
};
Used like this:
var result = _.filter(bag, negate(evens));
Or just declare a function when you call it:
var result = _.filter(bag, function(v) {return evens(v)});