lodash.debounce get previous function's call arguments - javascript

When _.debounce is called multiple times, it only apply with the last call's argument.
var f = _.debounce(function(a) {
console.log(a);
})
f(12)
f(1223)
f(5)
f(42)
//output -----> 42
Is there a way to get previous function's call arguments as well ?
ex:
var f = _.customDebounce(function(a) {
console.log(a);
})
f(12)
f(1223)
f(5)
f(42)
//output -----> [12, 1223, 5, 42]

I finaly made my own implementation of this function, extending lodash:
_.mixin({
debounceArgs: function(fn, options) {
var __dbArgs = []
var __dbFn = _.debounce(function() {
fn.call(undefined, __dbArgs);
__dbArgs = []
}, options);
return function() {
__dbArgs.push(_.values(arguments));
__dbFn();
}
},
throttleArgs: function(fn, options) {
var _thArgs = []
var _thFn = _.throttle(function() {
fn.call(undefined, _thArgs);
_thArgs = []
}, options);
return function() {
_thArgs.push(_.values(arguments));
_thFn();
}
},
})
Usage:
_.debounceArgs(function(a) {
console.log(a);
})

Here's a simplistic debounce that tracks its arguments. It doesn't try to mimic the last parameter of _.debounce
function customDebounce(func, wait) {
var args = [];
var timeoutId;
return function() {
// User formal parameters to make sure we add a slot even if a param
// is not passed in
if (func.length) {
for (var i = 0; i < func.length; i++) {
if (!args[i]) {
args[i] = [];
}
args[i].push(arguments[i]);
}
}
// No formal parameters, just track the whole argument list
else {
args.push(_.toArray(arguments));
}
clearTimeout(timeoutId);
timeoutId = setTimeout(function() {
func.apply(this, args);
args = [];
}, wait);
}
}
// For named arguments, each of the arguments becomes an array that tells
// you how many times it was called
var f = customDebounce(function(a, b, c) {
console.log('Debounced func called',
a.length,
'times', JSON.stringify({
a: a,
b: b,
c: c
}));
});
f(1, 3);
f(2, 3, 5);
f(3);
f(4, 2, 3);
// Debounced func called 4 times
// {"a":[1,2,3,4],"b":[3,3,null,2],"c":[null,5,null,3]}
// Can also be used with unnamed arguments, parameters are passed a little
// differently, each element in `arguments` is the array of arguments for
// that call, the latest one being at the end of the array
var g = customDebounce(function() {
console.log('Debounced no params func called ',
arguments.length,
'times',
JSON.stringify({
args: _.toArray(arguments)
}));
});
g(1, 3);
g(2, 3, 5);
g(3);
g(4, 2, 3);
// Debounced no params func called 4 times
// {"args":[[1,3],[2,3,5],[3],[4,2,3]]}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>

Related

Implementing a Number.prototype.loop(callback)

How to pass a reference of an argument, originally passed to a prototype function, on to a callback function which, too, is passed to the same prototype function?
The objective of the implementation is to loop for the value of the Number by executing the callback function.
Number.prototype.loop = function(index, callback) {
return Array(this).fill(0).forEach((e, i) => {
if(callback)
callback();
});
};
var num = 3;
num.loop(index, function(){
console.log(index);
});
Solution
Apparently, the reference to index is supposed to be passed directly to the callback function in order to indicate that the actual index property of the Array in the prototype function is to be passed to the callback function.
Number.prototype.loop = function(callback) {
return Array(this).fill(0).forEach((e, i) => {
if(callback)
callback(i);
});
};
var num = 3;
num.loop((index) => {
console.log(index);
});
There are 2 errors.
Pass the index i to callback in Number.prototype.loop function instead of the caller:
num.loop(index, function(index) { → num.loop(function(index) {
Number.prototype.loop = function(index, callback) { → Number.prototype.loop = function(callback) {
callback(); → callback(i);
Pass the numerical value of this instead of the instance itself to the Array constructor:
Array(this) → Array(this.valueOf())
Number.prototype.loop = function(callback) {
var num = this.valueOf();
return Array(num).fill(0).forEach((e, i) => {
if (callback)
callback(i);
});
};
var num = 3;
num.loop(function(index) {
console.log(index);
});
You can extend a Number native class with a function loop as the following. However, it is advised to not extend native as per this eslint's rule described.
Number.prototype.loop = function (callback) {
[...Array(this.valueOf())].forEach((_, idx) => {
callback(idx);
})
};
const num = 30;
num.loop((idx) => {
console.log(idx);
})

Get current value from an iterator

I was looking into javascript generators and iterators and was wondering if there is a way to write a generator function to return the value at the current position --- without of course having to call next() or to remember the returned value from the last next() call.
More specific, my failed attempt:
function* iterable(arr) {
this.index = 0;
this.arr = arr;
while(this.index < this.arr.length) {
yield this.arr[this.index++];
}
}
iterable.prototype.current = function () {
return this.arr[this.index];
}
const i = iterable([0, 1, 2]);
console.log(i.current()); // TypeError: Cannot read property 'undefined' of undefined
The desired functionality could be implemented using a class like this (I'm aware of the fact that the return values from the iterator would be objects like { value: 1, done: false }):
class iterableClass {
constructor(arr) {
this.index = 0;
this.arr = arr;
}
get(i) {
return this.index < arr.length ? this.arr[this.index] : false;
}
next() {
const val = this.get(this.index);
this.index++;
return val;
}
current() {
return this.get(this.index);
}
}
const i = iterableClass([0, 1, 2]);
console.log(i.current()); // 0
While I could just work with the class (or even a plain old function), I was wondering if this could be done with a generator/iterator or maybe there's an even better option.
The problem with your generator function is that a) it doesn't start running when you call it, it just creates the generator (this.arr and this.index won't be initialised until the first call to next()) and b) there is no way to access the generator object from inside the function like you tried with this.
Instead, you would want
function iterable(arr) {
const gen = Object.assign(function* () {
while (gen.index < gen.arr.length) {
yield gen.arr[gen.index++];
}
}(), {
arr,
index: 0,
current() {
return gen.arr[gen.index];
},
});
return gen;
}
Alternatively, instead of using generator syntax you can also directly implement the Iterator interface:
function iterable(arr) {
return {
arr,
index: 0,
current() { return this.arr[this.index]; },
next() {
const done = !(this.index < this.arr.length);
return { done, value: done ? undefined : this.arr[this.index++] };
},
[Symbol.iterator]() { return this; },
};
}
(which you could of course write as a class as well)
There seem to be multiple interpretations of this question. My understanding is that you want an iterator that provides a way to access the most recently-retrieved value, as shown by the last line in your final code block:
console.log(i.current()); // 0
Doing that isn't part of the iterator interface and isn't provided by generator functions. You could provide an iterator wrapper that did it, and then use that on the generator from the generator function (although you don't need a generator for what you're doing, the standard array iterator does it), see comments:
// Get the Iterator prototype, which has no global name
const itPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
);
function currentWrapper(source) {
// Allow source to be an iterable or an iterator
if (Symbol.iterator in source) {
source = source[Symbol.iterator]();
}
// Create our wrapper iterator
const it = Object.create(itPrototype);
// Remember the last value we saw from `next`
let current = null;
// The iterator method
it.next = () => {
return current = source.next();
};
// Our additional methods
it.current = () => current && current.value;
it.currentResult = () => ({...current});
return it;
}
This has the advantage of being reusable and generic, not tied to a specific iterable.
Live Example:
// Get the Iterator prototype, which has no global name
const itPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
);
function currentWrapper(source) {
// Allow source to be an iterable or an iterator
if (Symbol.iterator in source) {
source = source[Symbol.iterator]();
}
// Create our wrapper iterator
const it = Object.create(itPrototype);
// Remember the last value we saw from `next`
let current = null;
// The iterator method
it.next = () => {
return current = source.next();
};
// Our additional methods
it.current = () => current && current.value;
it.currentResult = () => ({...current});
return it;
}
// Something to iterate over
const a = [1, 2, 3];
// Example use #1: Using `current`
const it = currentWrapper(a[Symbol.iterator]());
console.log("current", it.current()); // undefined
console.log("next", it.next()); // {value: 1, done: false}
console.log("current", it.current()); // 1
console.log("currentResult", it.currentResult()); // {value: 1, done: false}
// Example use #2: Just normal use of an iterator
for (const value of currentWrapper(a)) {
console.log(value);
}
.as-console-wrapper {
max-height: 100% !important;
}
I focussed on the current bit and not the index bit because I think of iterables as streams rather than arrays, but I suppose it would be easy enough to add index. The slightly-tricky part is when the iterator has finished, do you increment the index when next is called or not? The below doesn't:
// Get the Iterator prototype, which has no global name
const itPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
);
function currentWrapper(source) {
// Allow source to be an iterable or an iterator
if (Symbol.iterator in source) {
source = source[Symbol.iterator]();
}
// Create our wrapper iterator
const it = Object.create(itPrototype);
// Remember the last value we saw from `next` and the current "index"
let current = null;
let index = -1;
// The iterator method
it.next = () => {
// Don't increase the index if "done" (tricky bit)
if (!current || !current.done) {
++index;
}
return current = source.next();
};
// Our additional methods
it.current = () => current && current.value;
it.currentResult = () => ({...current});
it.currentIndex = () => index;
return it;
}
// Something to iterate over
const a = [1, 2, 3];
// Example use #1: Using `current`
const it = currentWrapper(a[Symbol.iterator]());
console.log("current", it.current()); // undefined
console.log("next", it.next()); // {value: 1, done: false}
console.log("current", it.current()); // 1
console.log("currentResult", it.currentResult()); // {value: 1, done: false}
console.log("currentIndex", it.currentIndex()); // 0
console.log("next", it.next()); // {value: 2, done: false}
console.log("current", it.current()); // 2
console.log("currentResult", it.currentResult()); // {value: 2, done: false}
console.log("currentIndex", it.currentIndex()); // 1
// Example use #2: Just normal use of an iterator
for (const value of currentWrapper(a)) {
console.log(value);
}
.as-console-wrapper {
max-height: 100% !important;
}
Why not use a function from MDN Iterators and generators, where just the return part is replaced by the value instead of an object with value and done property
function makeIterator(array) {
var nextIndex = 0,
lastValue;
return {
next: function() {
return lastValue = nextIndex < array.length ? array[nextIndex++] : undefined;
},
last: function () {
return lastValue;
}
};
}
var it = makeIterator(['yo', 'ya']);
console.log(it.next());
console.log(it.next());
console.log(it.last());
console.log(it.next());

Problems with insertion into an array

vm.categorias = function () {
ConectaVagas('get', 'categorias').then(function (val) {
vm.categorias = val;
for (var a = 0; a < vm.categorias.length; a++) {
console.log(a); // Here returns 0,1,2,3 ( number with categories )
ConectaVagas('get', 'subcategoria', vm.categorias[a].id_categoria).then(function (val) { // List the subcategories related the categorie in loopfor
vm.subAplicar = val;
console.log(a); // Here returns the number 4 but i want returns 0, 1, 2, 3 and i do the insertion in arrayNova
vm.categoria[a].arrayNova = vm.subAplicar;
});
}
});
}
I need to insert into the object vm.category [a] an array (arrayNova) with all subcategories, but I am not following :(
You need to enclose in a new function the second call to ConnectVagas. Your variable a always prints to 4, because in javascript variables have a function scope. In order to correct this, create an anonymous function with parameters the current_item and a, so the value of a will not get overwritten because it creates a new function scope.
vm.categorias = function() {
ConectaVagas('get', 'categorias').then(function(val) {
vm.categorias = val;
for (var a = 0; a < vm.categorias.length; a++) {
console.log(a); // Here returns 0,1,2,3 ( number with categories )
var categorias_item = vm.categorias[a].id_categoria
(function(categorias_item, a) {
ConectaVagas('get', 'subcategoria', categorias_item.id_categoria).then(function(val) { // List the subcategories related the categorie in loopfor
vm.subAplicar = val;
console.log(a); // Here returns the number 4 but i want returns 0, 1, 2, 3 and i do the insertion in arrayNova
categorias_item.arrayNova = vm.subAplicar;
});
})(categorias_item, a);
}
});
}

JavaScript compose functions

I am reading a book which contains the following example:
var composition1 = function(f, g) {
return function(x) {
return f(g(x));
}
};
Then the author writes: "...naive implementation of composition, because it does not take the execution context into account..."
So the preferred function is that one:
var composition2 = function(f, g) {
return function() {
return f.call(this, g.apply(this, arguments));
}
};
Followed by an entire example:
var composition2 = function composition2(f, g) {
return function() {
return f.call(this, g.apply(this, arguments));
}
};
var addFour = function addFour(x) {
return x + 4;
};
var timesSeven = function timesSeven(x) {
return x * 7;
};
var addFourtimesSeven2 = composition2(timesSeven, addFour);
var result2 = addFourtimesSeven2(2);
console.log(result2);
Could someone please explain to me why the composition2 function is the preferred one (maybe with an example)?
EDIT:
In the meantime i have tried to use methods as arguments as suggested, but it did not work. The result was NaN:
var composition1 = function composition1(f, g) {
return function(x) {
return f(g(x));
};
};
var composition2 = function composition2(f, g) {
return function() {
return f.call(this, g.apply(this, arguments));
}
};
var addFour = {
myMethod: function addFour(x) {
return x + this.number;
},
number: 4
};
var timesSeven = {
myMethod: function timesSeven(x) {
return x * this.number;
},
number: 7
};
var addFourtimesSeven1 = composition1(timesSeven.myMethod, addFour.myMethod);
var result1 = addFourtimesSeven1(2);
console.log(result1);
var addFourtimesSeven2 = composition2(timesSeven.myMethod, addFour.myMethod);
var result2 = addFourtimesSeven2(2);
console.log(result2);
This just answers what composition2 actually does:
composition2 is used when you want to keep this as context in the functions itself. The following example shows that the result is 60 by using data.a and data.b:
'use strict';
var multiply = function(value) {
return value * this.a;
}
var add = function(value) {
return value + this.b;
}
var data = {
a: 10,
b: 4,
func: composition2(multiply, add)
};
var result = data.func(2);
// uses 'data' as 'this' inside the 'add' and 'multiply' functions
// (2 + 4) * 10 = 60
But yet, it still breaks the following example (unfortunately):
'use strict';
function Foo() {
this.a = 10;
this.b = 4;
}
Foo.prototype.multiply = function(value) {
return value * this.a;
};
Foo.prototype.add = function(value) {
return value + this.b;
};
var foo = new Foo();
var func = composition2(foo.multiply, foo.add);
var result = func(2); // Uncaught TypeError: Cannot read property 'b' of undefined
Because the context of composition2 (this) is undefined (and is not called in any other way, such as .apply, .call or obj.func()), you'd end up with this being undefined in the functions as well.
On the other hand, we can give it another context by using the following code:
'use strict';
var foo = new Foo();
var data = {
a: 20,
b: 8,
func: composition2(foo.multiply, foo.add)
}
var result = data.func(2);
// uses 'data' as 'this'
// (2 + 8) * 10 = 200 :)
Or by explicitly setting the context:
'use strict';
var multiply = function(value) {
return value * this.a;
};
var add = function(value) {
return value + this.b;
};
var a = 20;
var b = 8;
var func = composition2(multiply, add);
// All the same
var result1 = this.func(2);
var result2 = func.call(this, 2);
var result3 = func.apply(this, [2]);
composition1 would not pass arguments other than the first to g()
If you do:
var composition1 = function(f, g) {
return function(x1, x2, x3) {
return f(g(x1, x2, x3));
}
};
the function will work for the first three arguments. If you however want it to work for an arbitrary number, you need to use Function.prototype.apply.
f.call(...) is used to set this as shown in Caramiriel's answer.
I disagree with the author.
Think of the use-case for function-composition. Most of the time I utilize function-composition for transformer-functions (pure functions; argument(s) in, result out and this is irrelevant).
2nd. Utilizing arguments the way he does it leads into a bad practice/dead end, because it implies that the function g() might depend on multiple arguments.
That means, that the composition I create is not composable anymore, because it might not get all arguments it needs.
composition that prevents composition; fail
(And as a side-effect: passing the arguments-object to any other function is a performance no-go, because the JS-engine can't optimize this anymore)
Take a look at the topic of partial application, usually misreferenced as currying in JS, wich is basically: unless all arguments are passed, the function returns another function that takes the remaining args; until I have all my arguments I need to process them.
Then you should rethink the way you implement argument-order, because this works best when you define them as configs-first, data-last.Example:
//a transformer: value in, lowercased string out
var toLowerCase = function(str){
return String(str).toLowerCase();
}
//the original function expects 3 arguments,
//two configs and the data to process.
var replace = curry(function(needle, heystack, str){
return String(str).replace(needle, heystack);
});
//now I pass a partially applied function to map() that only
//needs the data to process; this is really composable
arr.map( replace(/\s[A-Z]/g, toLowerCase) );
//or I create another utility by only applying the first argument
var replaceWhitespaceWith = replace(/\s+/g);
//and pass the remaining configs later
arr.map( replaceWhitespaceWith("-") );
A slightly different approach is to create functions that are, by design, not intended to get all arguments passed in one step, but one by one (or in meaningful groups)
var prepend = a => b => String(a) + String(b); //one by one
var substr = (from, to) => value => String(str).substr(from, to); //or grouped
arr.map( compose( prepend("foo"), substr(0, 5) ) );
arr.map( compose( prepend("bar"), substr(5) ) );
//and the `to`-argument is undefined; by intent
I don't intend to ever call such functions with all the arguments, all I want to pass them is their configs, and to get a function that does the job on the passed data/value.
Instead of substr(0, 5, someString), I would always write someString.substr(0, 5), so why take any efforts to make the last argument (data) applyable in the first call?

how capture the arguments number need in a function (currying function partial application)

I am working on the curring function and partial application,
I am trying to improve the function schonfinkelize:
function schonfinkelize(fn){
var
slice = Array.prototype.slice,
stored_args = slice.call(arguments, 1);
return function(){
var
new_args = slice.call(arguments),
args = stored_args.concat(new_args);
return fn.apply(null, args);
}
}
This function permit to pass as argument a function and a part of the argument of the function passed as argument (partial application) so the first time you return a function and then when you fire the function again the result.
function add(x, y, z){
return x + y + z;
}
var val = schonfinkelize(add, 1, 2);
console.log( val(3) ) // console output--> 6
I want check inside schonfinkelize the number of arguments need to the function "add" (but it should work with every function) so I can choose when return another function or directly the result of the function "add".
bacause if I use schonfinkelize in this way:
var val2 = schonfinkelize(add, 1, 2, 3);
console.log( val2 ) // --> function
console.log( val2() ) // --> 6
I have to fire the function two time, instead a want avoid this behavior and define directly the value if the arguments are sufficient.
A possible solution could be the following:
function schonfinkelize(fn){
var
slice = Array.prototype.slice,
stored_args = slice.call(arguments, 1);
//* I have added this ********
if(fn.apply(null, stored_args))
return fn.apply(null, stored_args);
//****************************
return function(){
var
new_args = slice.call(arguments),
args = stored_args.concat(new_args);
return fn.apply(null, args);
}
}
Could be because it returns immediately the result if the fn.apply(null, stored_args) return something that is not "null" or "NaN" but I think is not really performant and then I want work with the arguments.
As long as you put in place a requirement that the parameters defined for the function passed reflect the actually number of arguments that are to be ultimately received, you can use the .length property of the function to do the comparison of passed arguments to anticipated arguments.
function schonfinkelize(fn) {
if (fn.length === arguments.length - 1)
return fn.apply(this, [].slice.call(arguments, 1));
var
slice = Array.prototype.slice,
stored_args = slice.call(arguments, 1);
return function(){
var
new_args = slice.call(arguments),
args = stored_args.concat(new_args);
return fn.apply(null, args);
}
}
Side note... you can avoid the .slice() if you cache the fn in a new variable, and overwrite the first argument with the this value, then use .call.apply()...
if (fn.length === arguments.length - 1) {
var func = fn;
arguments[0] = this;
return func.call.apply(func, arguments);
}
In strict mode browsers you could even avoid having the make the new variable since the parameters are no longer mapped to changes in the arguments. But this doesn't work in browsers that don't support strict mode.
I don't think there is a correct way to determine number of arguments for arbitrary function. I prefer to store len in function if it is necessary, and check if it is defined, and if it is and if fn.len == stored_args.length then return function that just returns value.
function schonfinkelize(fn){
var
slice = Array.prototype.slice,
stored_args = slice.call(arguments, 1);
if (fn.len != undefined && fn.len == stored_args.length) {
var val = fn.apply(null, stored_args);
return function () {
return val;
};
}
return function () {
var
new_args = slice.call(arguments),
args = stored_args.concat(new_args);
return fn.apply(null, args);
};
}
var f = function (a, b, c) {
return a + b + c;
};
f.len = 3;
var g = schonfinkelize(f, 1, 2);
alert(g); // function () { var new_args = slice.call(arguments), args = stored_args.concat(new_args); return fn.apply(null, args); };
alert(g(3)); // 6
var g = schonfinkelize(f, 1, 2, 3);
alert(g); // function () { return val; };
alert(g()); // 6
I want propose also a personal evolution of the code but I have to said thanks to squint to has resolved the problem, simply suggest me to use the property .length.
The next level it is in my opinion permit to create a partial function able to be called every time you want until you finish to fill all the arguments, I have also simplified the code:
function schonfinkelize(fn, stored_args){
if(fn.length == stored_args.length)
return fn.apply(null, stored_args);
return function(){
var
new_args = arguments[0],
args = stored_args.concat(new_args);
if(fn.length == args.length)
return fn.apply(null, args);
return schonfinkelize(fn, args);
}
}
function add(x, y, w, z){
return x + y + w + z;
}
var
val = schonfinkelize(add, [1, 2, 3, 4]),
val2 = schonfinkelize(add, [1, 2]),
val3 = schonfinkelize(add, [1]);
// checking
console.log(val); // output --> 10 // called only 1 time
console.log(val2([3, 4])); // output --> 10 // called in 2 times
val3 = val3([2]);
val3 = val3([3]);
console.log(val3([4])); // output --> 10 // called 4 times!

Categories