Memoize callback function - javascript

I was trying this memoization on
function getData(n, m, callback){
callback(n+m);
};
let memoizedData = memoize(getData);
console.log(memoizedData(5, 4, (result)=>{})) // 9;
I want to cache the result i,e, 9 so whenever next call happens it takes from cache.
I understand memoization if getData would have return n+m instead of putting to the callback.
Any help will be appreciated.
Edits: Want to implement this via vanillaJs.
Edited the above question for more clarity
function getData(n, m, callback){
callback(n+m);
};
let memoizedData = memoize(getData);
memoizedData(5, 4, (result)=>{console.log(result)}) // 9;
memoizedData(5, 4, (result)=>{console.log(result)}) // 9 - returned from cache

If I understand correctly, you are asking for an implementation of memoize. As to your console.log: this is strange, since the result is not returned, but passed as an argument to a callback. So you should actually print within that callback. You could even pass console.log as callback.
For the implementation of memoize, I will assume that the function to memoize will always have a callback as last parameter.
First, memoize should return a function which accept the same arguments as the function that is memoized.
The idea is to maintain a Map, keyed by the arguments that are passed to the function (excluding the callback argument), and with the corresponding result. One way to build the key is to form the JSON representation of the arguments.
When memoize is called, and the arguments form a key that is already present in the map, then call the callback with the corresponding value from the map. If not present, then call the function with the same set of arguments, except for the callback. Let the callback be a function that stores the result in the map, and then passes the result on to the callback that was passed to memoize.
Here is how you could write it:
function memoize(func) {
let map = new Map; // for memoization
return function(...args) {
let callback = args.pop(); // last argument must be a function
let key = JSON.stringify(args);
let result = map.get(key);
if (result !== undefined) {
console.log(`getting result from map.get('${key}')`);
return callback(result);
}
console.log(`calling ${func.name}(${args.join(", ")}, callback)`);
func(...args, result => {
console.log(`writing result with map.set('${key}', ${result})`);
map.set(key, result);
callback(result);
});
}
}
function getData(n, m, callback){
callback(n+m);
}
let memoizedData = memoize(getData);
memoizedData(5, 4, console.log) // 9 calling getData;
memoizedData(5, 4, console.log) // 9 from memory;
Note that this:
memoizedData(5, 4, console.log)
...is just "short" for:
memoizedData(5, 4, (result) => console.log(result))
The only difference is that there is an extra wrapper function around console.log, but practically there is no real difference.

If your asking how to implement memoization you may implement it like this
import { useMemo, useCallback } from 'react';
export default function Index() {
const getData = useCallback((n, m, callback) => callback(n + m), []);
let memoizedData = useMemo(() => getData(5, 4, (result) => result), [
getData,
]);
console.log(memoizedData);
return null;
}

Related

Calling a function next to another function javascript explanation

Can someone please explain to me this bit of code
return pipe(...fns)(this);
I understand if we didn't have (this), so we returned the reduced functions but (this) confused me.
Full source code here. line 72
https://stackblitz.com/edit/typescript-hv4ntb
It is the same as this:
const pipeFunction = pipe(...fns); // pipe returns a function
const result = pipeFunction(this); // call the returned function with this as argument
return result; // return the result
So if you ever see something like variable(...)(...) you should assume that the variable evaluates to a function that returns a function, like this perhaps:
const variable = (a) => (b) => a + b;
variable(4)(2);
// ==> 6
const partial = variable(8)
[1, 2, 3].map(partial);
// ==> [9, 10, 11]
Javascript is said to have first-class functions (you can read more here on MDN), meaning that you can do with functions what you can do with other types. Storing them into a variable, passing them as argument to other functions, as well as using them as return value from other functions.
The thinking is, just think of functions as values.
In your use-case, a function is just being returned from another function:
function multiply(times) {
return function(number) {
return number * times;
};
}
// as you can see, we are storing the "returned function"
// resulted from calling `multiply` into the `double` variable.
const double = multiply(2);
console.log(
double(5)
);
Now, of course, you could short-circuit the double variable,
and just call the returned function straight away.
function multiply(times) {
return function(number) {
return number * times;
};
}
console.log(
multiply(2)(5)
);

Writing _.Memoize from underbar in Javascript

I am currently studying Javascript and am doing the underbar project(re-writing the _underbar library).
I have solved all of them but one, as I am stuck on _.memoize
This is my current code
_.memoize = function(func) {
var cache = {};
var slice = Array.prototype.slice;
return function() {
var args = slice.call(arguments);
if (args in cache) {
return cache[args];
} else {
return (cache[args] = func.apply(this, args));
}
}
};
This is the test case I am failing to pass
// Here, we wrap a dummy function in a spy. A spy is a wrapper function (much like _.memoize
// or _.once) that keeps track of interesting information about the function it's spying on;
// e.g. whether or not the function has been called.
it('should run the memoized function twice when given an array and then given a list of arguments', function() {
// Be careful how you are checking if a set of arguments has been passed in already
var spy = sinon.spy(function() { return 'Dummy output'; });
var memoSpy = _.memoize(spy);
memoSpy([1, 2, 3]);
expect(spy).to.have.been.calledOnce;
memoSpy(1, 2, 3);
expect(spy).to.have.been.calledTwice;
});
Or in words ' It should run the memoized function twice when given an array and then given a list of arguments'
I tried to change the args by checking if the first was a array and if not making all the arguments an array, but to no luck. I also tried to re write the code to hold a result and use Fibonacci to store the cache but it would run the function twice.
How would I resolve this test case?
Thanks
Using cache[args] doesn't make much sense because args is an array, and using bracket notation will convert it to a string. You could make an array of arguments arrays to their return values, then compare the arrays:
const _ = {};
_.memoize = function(func) {
const allArgs = [];
return function(...args) {
// Try to find matching arguments in allArgs
const result = allArgs.find(
item => item.args.length === args.length && item.args.every(
(arg, i) => args[i] === arg
)
);
// If a matching argument array was found, return the result:
if (result) return result.returnValue;
const returnValue = func.apply(this, args);
allArgs.push({ args, returnValue });
return returnValue;
}
};
const memoizedSum = _.memoize((...args) => {
console.log('called with', args);
return args.reduce((a, b) => a + b, 0);
});
console.log(memoizedSum(1, 2, 3));
console.log(memoizedSum(1, 2, 3));
console.log(memoizedSum(4, 5, 6));
Another option with less computational complexity would be to create a Map for each argument. For example, a call with arguments of 1, 2, 3 could result in a data structure of:
Map([[
1, { nested: Map([[
2, { nested: Map([[
3, { result: 6 }
]])}
]])}
]])
Then you'd find whether a nested result exists for the given arguments by recursively iterating through the Maps with .get for each argument, instead of iterating through all arguments the function has been called with so far. The code would be complicated, but it'd be more efficient.
Your function looks good, just one little detail. In your last return you should cache it first and then return it:
cache[args] = func.apply(this, args);
return cache[args];

Confusion with how thenable callback works in Promise?

I am new to JS and was learning promises. The code excerpt I want to show is this
promisedFunction.then(data=>console.log(data))
or simply
promisedFunction.then(console.log)
which is equivalent of the former code excerpt. The question is how is that possible to just use then(console.log) instead of then(data=>console.log(data))? Does it mean that we can omit the passed-from-promise data in thenable callback?
data=>console.log(data) is a function that takes a parameter and calls a method with the passed in argument.
If you pass console.log it will execute the same method and still pass the same argument.
In effect you are dropping the extra function call - slightly more abstractly, imagine this:
//some function `f`
const f = x => x + 1;
//different function `g` that just forwards the call to `f`:
const g = x => f(x);
console.log(g(41)); //42
The definition of g is just a function that all it does is call f. Which has exactly the same effect as the f function. So, we can just re-write it as:
//some function `f`
const f = x => x + 1;
//different function `g` that just forwards the call to `f`:
const g = f;
console.log(g(41)); //42
and get exactly the same effect.
In Lambda Calculus, this removal of an essentially empty "wrapper function" is called Eta reduction.
Therefore, yes, both .then(data => console.log(data)) and .then(console.log) do exactly the same, you're performing the same sort of conversion where you're taking out a dummy forwarding function.
However, that's not always an option. For example, if removing the forwarding function will end up calling the target with more parameters, you can get different behaviour. An infamous example is parseInt when used in .map:
const arrayOfString = ["1", "2", "3"];
const arrayOfIntegers1 = arrayOfString.map(parseInt);
const arrayOfIntegers2 = arrayOfString.map(x => parseInt(x));
console.log("arrayOfIntegers1", arrayOfIntegers1);
console.log("arrayOfIntegers2", arrayOfIntegers2);
The issue is that Array#map calls the callback with three parameters - the item, the index, and the array. And parseInt has an optional second parameter - if passed in, it changes how the number is parsed. So you get NaN.
The easiest way to observe this is with console.log:
const arrayOfString = ["1", "2", "3"];
console.log(" --- using just `console.log` as callback ---");
arrayOfString.map(console.log);
console.log(" --- using just `x => console.log(x)` as callback ---");
const arrayOfIntegers2 = arrayOfString.map(x => console.log(x));
So, dropping the intermediate function works as long as it has the same arity as what it will be called with and what the next function will be called with.
data => console.log(data) is a function which takes in data and then calls the function console.log with data as its argument. So, it's a function which gets given data and then it gives that exact same data argument to the console.log function.
As you know console.log is a function, so, you can place console.log instead of your arrow function. This way, it will be called with the data argument and any other arguments natively passed into .then()'s onFulfilled callback (in this case it only gets given the resolved value of the promise).
Take the following example below. bar gets given "hello", which it gives to foo, foo accepts "hello" and returns "hello" back to where it was called, so, bar ends up returning "hello":
const foo = x => x;
const bar = x => foo(x);
console.log(bar("hello"));
In the above example, bar acts somewhat like a middle-man, as all it does is pass x to foo. This middle-step can be removed, by straight-up assigning bar to foo. This way, when we call bar(), we are executing the code defined within the function foo. This is the same idea that allows you to remove the need to explicitly defined your arrow function in .then():
const foo = x => x;
const bar = foo;
console.log(bar("hello"));
This style of coding is known as point-free style code and, in this case, is achieved by eta-reduction.
The return value of promise or the "resolved value" is the input to callback passed in then.
This behavior is not specific to promise but even [1,2,3].forEach(console.log) behaves the same where the 3 arguments of forEach are passed to console log. Thus you get ,
1 0 Array(3) [ 1, 2, 3 ]
2 1 Array(3) [ 1, 2, 3 ]
3 2 Array(3) [ 1, 2, 3 ]
promisedFunction.then expects a function to which will receive the data as a parameter.
in the case of data=>console.log(data) is an array function equal to:
function(data) {
console.log(data);
}
and console.log is also a function which prints in console what receives as a parameter.
That's why console.log works.
If you had:
var print = function(data) {
console.log(data);
}
promisedFunction.then(print) would also work.
See this working example that will resolve after 2 secs:
const doSomething = function(){
return new Promise((resolve, reject) => {
let wait = setTimeout(() => {
resolve('Promise resolved!');
}, 2000)
})
};
var print = function(data) {
console.log(data);
}
doSomething().then(print)

How can I return the last item in a Array object

I am trying to learn callbacks functions by simply creating a function that takes two numbers and has a callback that returns the last item in my array. I am a self taught web developer so I would love some experts to educate me if possible.
This is my code
const items = ['Pencil', 'Notebook', 'yo-yo', 'Gum'];
function last(arr, cb) {
// last passes the last item of the array into the callback.
// console.log(arr.pop())
return cb(arr[arr.length - 1])
}
last(items, cb)
my error is: Uncaught TypeError: cb is not a function
If you want to use callback cb, then you need to define it. Like this
const items = ['Pencil', 'Notebook', 'yo-yo', 'Gum'];
const cb = x => {
console.log('last item is:', x);
return x;
}
function last(arr, cb) {
return cb(arr[arr.length - 1])
}
last(items, cb);
You need to create a callback. Use the code below:
const items = ['Pencil', 'Notebook', 'yo-yo', 'Gum'];
function last(arr, cb) {
// last passes the last item of the array into the callback.
// console.log(arr.pop())
return cb(arr[arr.length - 1]);
}
last(items, e => { console.log(e) });
A callback is a function which is passed to another function as an argument and which is called from within it to continue the program. You're missing that callback function in your code. That's the simple bit.
Now, this sentence from your question needs a little more work:
a function that takes two numbers and has a callback that returns the last item
This is certainly fine for cases such as your example where there are no asynchronous processes involved...
const items = ['Pencil', 'Notebook', 'yo-yo', 'Gum'];
function last(arr, cb) {
const el = arr[arr.length - 1];
return cb(el);
}
const lastItem = last(items, function print(el) {
return `Last element is ${el}`;
});
console.log(lastItem);
...but generally you'll see that callbacks are primarily used to continue the flow of code after an asynchronous process has run, and you can't return values from a callback in that situation.
For example in this example we're using a setTimeout to delay calling the callback for 2s:
const items = ['Pencil', 'Notebook', 'yo-yo', 'Gum'];
function last(arr, cb) {
const el = arr[arr.length - 1];
setTimeout(() => {
cb(el);
}, 2000);
}
last(items, function print(el) {
console.log(`Last element is ${el}`);
});
We're not returning anything because returning the setTimeout makes no sense, neither does returning the callback from within the setTimeout. Instead we call the callback with the value after 2s and the string is logged to the console.
It means that we can't return a value to a variable like we did with lastItem in the first example, and the reason why "How do I return the response from an asynchronous call" is likely the most linked question on this site, and it was important that you understood that for your understanding of callbacks.
So, while you can return values from callbacks they are generally used continue the execution of a program after an async process.
Hope that helps.

Debounce a function with argument

I'm trying to debounce a save function that takes the object to be saved as a parameter for an auto-save that fires on keystroke. The debounce stops the save from happening until the user stops typing, or at least that's the idea. Something like:
var save = _.debounce(function(obj) {
...
}, delay);
Where this falls apart is if I try to save two objects in quick succession. Because the debounce doesn't take the passed in object into account, only the second call to save will fire and only one object will be saved.
save(obj1);
save(obj2);
Will only save obj2, for example.
I could make obj an instance of a class that has its own save method that takes care of debouncing saves to just that object. Or keep a list of partial/curried functions somewhere, but I'm hoping there's a one stop solution out there. Something like:
var save = _.easySolution(function(obj) {
...
}, delay);
What I'm looking for the following string of saves to save each object, but only save each object once.
save(obj1);
save(obj2);
save(obj3);
save(obj2);
save(obj2);
save(obj3);
save(obj2);
save(obj1);
EDIT: Something like this, maybe, just not so convoluted, and something that doesn't mutate the obj with a __save function?
function save(obj) {
if(!obj.__save) {
obj.__save = _.debounce(_.partial(function(obj) {
...
}, obj), delay);
}
obj.__save();
}
You're going to want to create a debounced version of the function for each argument that get's passed. You can do this fairly easily using debounce(), memoize(), and wrap():
function save(obj) {
console.log('saving', obj.name);
}
const saveDebounced = _.wrap(
_.memoize(() => _.debounce(save), _.property('id')),
(getMemoizedFunc, obj) => getMemoizedFunc(obj)(obj)
)
saveDebounced({ id: 1, name: 'Jim' });
saveDebounced({ id: 2, name: 'Jane' });
saveDebounced({ id: 1, name: 'James' });
// → saving James
// → saving Jane
You can see that 'Jim' isn't saved because an object with the same ID is saved right after. The saveDebounced() function is broken down as follows.
The call to _memoize() is caching the debounced function based on some resolver function. In this example, we're simply basing it on the id property. So now we have a way to get the debounced version of save() for any given argument. This is the most important part, because debounce() has all kinds of internal state, and so we need a unique instance of this function for any argument that might get passed to save().
We're using wrap() to invoke the cached function (or creating it then caching it if it's the first call), and pass the function our object. The resulting saveDebounced() function has the exact same signature as save(). The difference is that saveDebounced() will debounce calls based on the argument.
Note: if you want to use this in a more generic way, you can use this helper function:
const debounceByParam = (targetFunc, resolver, ...debounceParams) =>
_.wrap(
_.memoize(
() => _.debounce(targetFunc, ...debounceParams),
resolver
),
(getMemoizedFunc, ...params) =>
getMemoizedFunc(...params)(...params)
)
// And use it like so
function save(obj) {
console.log('saving', obj.name);
}
const saveDebounced = debounceByParam(save, _.property('id'), 100)
Maybe something like:
var previouslySeen = {}
var save = _.debounce(function(obj) {
var key = JSON.stringify(obj);
if (!previouslySeen[key]) {
previouslySeen[key] = 1;
} else {
...
}
}, delay);
You can use internal object in closure for set/get debounced function.
In this example we check if debounced function with this args alredy saved in our memory object while call debounced function. If no - we create it.
const getDebouncedByType = (func, wait, options) => {
const memory = {};
return (...args) => {
// use first argument as a key
// its possible to use all args as a key - e.g JSON.stringify(args) or hash(args)
const [type] = args;
if (typeof memory[searchType] === 'function') {
return memory[searchType](...args);
}
memory[searchType] = debounce(func, wait, { ...options, leading: true }); // leading required for return promise
return memory[searchType](...args);
};
};
Original gist - https://gist.github.com/nzvtrk/1a444cdf6a86a5a6e6d6a34f0db19065

Categories