Optional parameter in Arrow Function - javascript

I have a handler like
handleSelect = (k0, k1, v) => {
...
}
};
And I want to make k1 here optional. Is there a good way?

There is no good way. This isn't specific to React or arrows.
A variadic function with optional parameter in the middle requires to parse arguments:
handleSelect = (...args) => {
let k0, k1, v;
if (args.length > 2) {
[k0, k1, v0] = args;
} else {
[k0, v0] = args;
k1 = 'default';
}
...
};
This may result in obscure API. A better recipe for a function with several parameters some of which can be optional is to accept an object with options. A function doesn't depend on parameter order this way:
handleSelect = ({ k0, k1 = 'default', v }) => {
...
};

Related

Javascript: print function call

I have a function like so:
const ILog = <T>(value: T): T => {
console.log(value);
return value;
}
It acts like console.log with the added benefit that it keeps the value it had.
However, I'd like to differenciate between calls of the same function:
let a = false;
let b = false;
let c = false;
ILog(a);
ILog(b);
ILog(c);
// current
$ node file.js
false
false
false
// expected
$ node file.js
ILog(a): false
ILog(b): false
ILog(c): false
Can some javascript black magic achieve this?
An example of how this would work would be like this:
const ILog = (name: string) => <T>(value: T): T => {
console.log(`ILog(${name})`, value);
return value;
}
ILog("[some_expression]")([some_expression]);
$ node file.js
ILog([some_expression]): [result]
Without repeating the expression in string form.
There's no magic way to do this, you could do something like this if you really wanted:
const ILog = <T>(value: T, argumentName: string): T => {
console.log(`ILog(${argumentName}): ${value}`);
return value;
}
const a = false;
ILog(a, "a");
Or using your existing ILog function,
ILog(`ILog(a): ${a}`);

Memoize a curried function

const f = (arg1) => (arg2) => { /* returns something */ }
Is it possible to memoize f with regard to the 2 arguments, namely:
f(1)(2);
f(1)(3); // Cache not hit
f(4)(2); // Cache not hit
f(1)(2); // Cache hit
You could take a Map as cache and take nested maps for all following arguments.
This cache works for arbitrary count of arguments and reuses the values from the former calls.
It works by taking a curried function and an optional Map. If the map is not supplied, a new map is created which serves as base cache for all other calls of the returned closure or the final result.
The inner function takes a single argument and checks if this value is in the map.
If not, call the curried function and check the returned value
if function, create a new closure over the function and a new map,
if no function take the result,
as value for a new element of the map.
Finally return the value from the map.
const cached = (fn, map = new Map()) => arg => {
const inCache = map.has(arg);
const hint = inCache ? 'in cache' : 'not in cache';
console.log(arg, hint);
if (!inCache) {
const value = fn(arg);
const result = typeof value === 'function' ? cached(value, new Map()) : value;
map.set(arg, result);
}
return map.get(arg);
};
const f = a => b => c => a * b * c; // the original curried function
const g = cached(f); // its cached variant
console.log(g(1)(2)(5)); // not not not 10
console.log(g(1)(3)(4)); // in not not 12
console.log(g(4)(2)(3)); // not not not 24
console.log(g(1)(2)(6)); // in in not 12
console.log(g(4)(2)(3)); // in in in 24
.as-console-wrapper { max-height: 100% !important; top: 0; }
Interesting question — you could have independent caches for each function. The cache on the outside function will hold functions. Each inside function could get its own independent cache. So calling f(10)(1) followed by f(10)(2) would result in calling a cached version of the inside function. Calling f(10)(1) again would hit both caches:
function getCachedF() {
// outer cache holds functions keyed to argument
let outer_memo = {}
const f = (arg1) => {
if (!outer_memo.hasOwnProperty(arg1)) {
// Create inner function on outer cache
// each inner function needs its own cache
// because it will return different values
// given different outer function calls
let inner_memo = {}
console.log("outer cache miss")
outer_memo[arg1] = (arg2) => {
// just a normal memoized function
// cache is simple key:value pair
if (!inner_memo.hasOwnProperty(arg2)) {
console.log("inner cache miss")
inner_memo[arg2] = arg1 + arg2
}
return inner_memo[arg2]
}
}
return outer_memo[arg1]
}
return f
}
let f = getCachedF()
// both caches miss
console.log("3+5", f(3)(5))
// cached result
console.log("3+5", f(3)(5))
// only inside cache hit
console.log("3+8", f(3)(8))
// inside cache only hits if both args are the same
console.log("10+8", f(10)(8))
Another alternative would be to have single cache with keys that are a combination of both arguments, but then the inner function would always have to be called.
This probably isn't the canonical memoization function.
A function that needs to cache its result is given a cache function that is used to store and retrieve previous results:
const sum = memo(cache => a => b => cache(`${a}+${b}`, () => a + b));
// ^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^
// A B C
A — The cache function is provided by the memo function.(A memoized function can opt-out from caching some results if necessary.)
B — A unique key for the result. (e.g. cache['1+2'] = 3)
C — A thunk that returns the result.(So we can check if we have it already before computing it.)
This supports both curried and non-curried functions but also functions that return a function as a value.
The memo function can be implemented as follow:
const memo = fn => {
const ns = Symbol();
const cache = (key, thunk) => cache[ns][key] ??= thunk();
cache[ns] = {};
return fn(cache);
};
I quite like the logical nullish assignment operator for managing the cache:
a ??= answer()
The expression on the right is evaluated and assigned to a if and only if a is not already defined. Then it returns the value of a:
const answer = () => (console.log('returning the answer'), 42);
let a;
a ??= answer();
//=> LOG: returning the answer
//=> 42
a ??= answer();
//=> 42
a ??= 40;
//=> 42
I've used a symbol to hide the actual cache set on the cache function. A symbol is not returned when enumerating properties of an object:
const foo = {};
const key1 = Symbol();
const key2 = 'bar';
foo[key1] = 42;
foo[key2] = 41;
Object.keys(foo);
//=> ['bar']
Object.entries(foo);
//=> [['bar', 41]]
Demo
// curried memoized function
const sum = memo(cache => a => b =>
cache(`${a}+${b}`,
() => (console.log(`computing ${a}+${b}…`), a+b)));
console.log(sum(1)(2));
console.log(sum(1)(2));
console.log(sum(1)(2));
// non-curried memoized function
const mul = memo(cache => (a, b) =>
cache(`${a}*${b}`,
() => (console.log(`computing ${a}*${b}…`), a*b)));
console.log(mul(2, 3));
console.log(mul(2, 3));
console.log(mul(2, 3));
// function-returning function
const deferred_sum = memo(cache => a => b =>
cache(`${a}+${b}`,
() => (console.log(`defer computing ${a}+${b}…`), () => a+b)));
console.log(deferred_sum(1)(2)());
console.log(deferred_sum(1)(2)());
console.log(deferred_sum(1)(2)());
<script>
const memo = fn => {
const ns = Symbol();
const cache = (key, thunk) => cache[ns][key] ??= thunk();
cache[ns] = {};
return fn(cache);
};
</script>
You can not to pass map to every function.
You can do like the next:
const memoize = fn => {
const cache = {};
return (...args) => {
const curriedFn = fn(...args);
return (...next) => {
const key = // generate your key
if (key in cache) return cache[key];
return (cache[key] = curriedFn(...next));
}
}
}

Reproduce without using spread arguments (...) when passing arguments through to a function call

I have been learning about spread arguments and I found it rather surprising that when using: cur.func.call(null, ...cur.arg, acc), args) that if you have an empty array no argument is passed to add().
Is it possible to reproduce this without using the ... seen in this line of code cur.func.call(null, ...cur.arg, acc), args)
class Lazy {
constructor() {
this.builtUpFuncs = [];
}
add(...newArgs) {
console.info(newArgs)
this.builtUpFuncs.push({
func: newArgs[0],
arg: typeof newArgs[1] === "undefined" ? [] : [newArgs[1]],
});
return this;
}
evaluate(target) {
return target.map((args) =>
this.builtUpFuncs.reduce((acc, cur) =>
cur.func.call(null, ...cur.arg, acc), args)
);
}
}
const lazyClass = new Lazy();
const returnValue =
lazyClass
.add(function timesTwo(a) { return a * 2; })
.add(function plus(a, b) { return a + b; }, 1)
.evaluate([1, 2, 3]);
console.info(returnValue);
If you want to avoid the spread syntax, the traditional way is to use apply instead of call:
cur.func.apply(null, cur.arg.concat(acc))
Note that the args part is the second argument to reduce, not this function call.
In either syntax it is normal that if cur.arg is an empty array, the only argument passed is acc.

Get to intermediate values in middle of a functional programming chain

I'm wondering if there's a concise or specific way to access values in the middle of an FP chain in JavaScript. Example:
const somestuff = [true, true, false];
let filteredCount = 0;
somestuff.filter((val) => val)
.forEach((val) => console.log(val));
Above, I'd like to set filteredCount to the length of the array returned by the filter function. The most straight-forward way is:
const somestuff = [true, true, false];
const filteredStuff = somestuff.filter((val) => val);
let filteredCount = filteredStuff.length;
filteredStuff.forEach((val) => console.log(val));
This is certainly valid but it breaks our FP chain and introduces an additional holding variable. I'm wondering if there's a convention for accessing values in the middle of the chain. Something like .once() that runs once and implicitly returns the value passed in, but nothing like that exists.
For debugging, I often use a function called tap to temporarily add a side-effect (like your console.log) to a function:
const tap = f => x => (f(x), x);
This function returns whatever it is passed, but not before calling another function with the value. For example:
const tap = f => x => (f(x), x);
const tapLog = tap(console.log);
const x = tapLog(10);
console.log("x is", x);
Your snippet basically does this:
Filter a list
(log the list)
Retrieve a length property from an array
If you construct this function using pipe or compose, you can "inject" the console.log in between without interrupting the data flow:
const countTrues = pipe(
filter(isTrue),
prop("length")
);
const countTruesWithLog = pipe(
filter(isTrue),
tap(console.log),
prop("length")
);
In a snippet:
// Utils
const isTrue = x => x === true;
const prop = k => obj => obj[k];
const tap = f => x => (f(x), x);
const filter = f => xs => xs.filter(f);
const pipe = (...fns) => x => fns.reduce((res, f) => f(res), x);
// Logic:
// Filter an array using the isTrue function
// and return the length of the result
const countTrues = pipe(
filter(isTrue),
prop("length")
);
// Create a filter with a console.log side-effect
// and return the length of the result
const countTruesWithLog = pipe(
filter(isTrue),
tap(console.log),
prop("length")
);
// App:
const somestuff = [true, true, false];
console.log("pure:");
const countA = countTrues(somestuff)
console.log(countA);
console.log("with log:")
const countB = countTruesWithLog(somestuff);
console.log(countB);
The reason there's no Array.prototype method like that, is that it has a side effect. This is something that is specifically avoided in functional programming.
However if you don't care about writing 'Pure Functions', or even the functional paradigm, you could put the side effect in your callbacks, or write a function in the Array prototype.
ie.
Array.prototype.once = function(callback) {
callback(this)
return this
}
You also have other hacky options like in the other answer
I don't think there's something like that by default. What you can do is extend Array, but I'm not really fond of extending framework classes (clashes with other once implementations for example). In this case you'd end up with:
Array.prototype.once = function once(func) {
func(this);
return this;
}
which is called like:
var filteredStuff = somestuff
.filter((val) => val)
.once(function(array) {
console.log(array.length);
})
.forEach((val) => console.log(val));
On the other hand, you can try to use default functions. One of these function that can access all items at once is reduce. Define a function once, that will call its first parameter once (:)) and you'd end up with something like:
function once(func) {
return function(accumulator, currentValue, currentIndex, array) {
if(currentIndex === 1) {
func(array);
}
return array;
}
}
which you'd be able to call like this:
var filteredStuff = somestuff
.filter((val) => val)
.reduce(once(function(array) {
console.log(array.length);
}), [0])
.forEach((val) => console.log(val));
Notice the ugly [0] to ensure once calls the passed function at least once (empty array included).
Both solutions aren't too neat, but it's the best I can come up with given the criteria.

module.exports multiple functions in Jest testing

After reading the Jest documentation, when it's mentioned that to export a single function from a tested file they show the following example:
function sum(a, b) {
return a + b;
}
module.exports = sum;
Now, if I have multiple specific functions I want to export on my tested file, like this:
function sum(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
function subtract(a, b) {
return a - b;
}
module.exports = sum;
module.exports = multiply;
The multiply function is the only one being exported. How can I make these function be exported? Or only part of my file?
You can do something like this :
module.exports = {};
module.exports.sum = function sum(a, b) {
return a + b;
}
module.exports.multiply = function multiply(a, b) {
return a * b;
}
module.exports.subtract = function subtract(a, b) {
return a - b;
}
End you use it like this:
var MyMathModule = require('./my_math_module');
MyMathModule.sum(a, b);
MyMathModule.multiply(a, b);
MyMathModule.subtract(a, b);
First, in your example, all you are doing there is overriding the exports object with a function ( which is totally fine )
The exports and module.exports are an object and are actually the same object ( i.e. module.exports === exports // true )
To do what you want you can do this a couple ways:
exports.sum = sum
exports.multiply = multiply
or
module.exports = { sum: sum, multiply: multiply } // etc
or
module.exports.sum = sum
module.exports.multiply = multiply
Having in mind the answer to this question, i'll paste here 2 ways to do the same thing.
For example, you have the JS file called exercise5, like this:
//You can create an object with functions, as follows:
const wordAnalysis = {
type: (word) => typeof (word),
whiteSpaces: (word) => {
let wordAnalysis = word.includes(' ')
if (wordAnalysis) {
return 'It has spaces'
} else {
return "It doesn't has spaces"
}
}
}
//Or you can create several single functions, like the following:
function numberAnalysis(word) {
let isANumber = typeof (word) === 'number' ? true : false
return isANumber
}
// în order to avoid overwriting the module.exports, it is needed to do one of the following (I chose the first one):
// 1)
module.exports.firstPlace = wordAnalysis
module.exports.secondPlace = numberAnalysis
// 2)
// module.exports = {
// functions: functions,
// isANumber: isANumber
// }
// 3)
// exports.functions = functions
// exports.isANumber = isANumber
// 4)
// exports = {
// functions: functions,
// isANumber: isANumber
// }
Now the file test named exercise5.test.js:
const wordAnalysis = require('./exercise5')
const numberAnalysis = require('./exercise5')
test('It should give me the type of what was typed', () => {
expect(wordAnalysis.firstPlace.type('teste')).toEqual('string')
})
test('It should give me the type of what was typed', () => {
expect(wordAnalysis.firstPlace.type(22)).toEqual('number')
})
test("It should give true if what is typed has at least a space or false if it doesn't", () => {
expect(wordAnalysis.firstPlace.whiteSpaces('Jon is cool')).toEqual('It has spaces');
})
test("It should give true if what is typed has at least a space or false if it doesn't", () => {
expect(wordAnalysis.firstPlace.whiteSpaces('AllTogetherNow')).toBe("It doesn't has spaces");
})
test('it should analyse if the given expression is a number or not', () => {
expect(numberAnalysis.secondPlace(2)).toBeTruthy()
})
test('it should analyse if the given expression is a number or not', () => {
expect(numberAnalysis.secondPlace('jon')).toBeFalsy()
})
The only thing you need to be aware is to export/import the correct object/function, and of course call it when your are developing the test.

Categories