Skip to content

Instantly share code, notes, and snippets.

@joedski
Created July 14, 2017 00:07
Show Gist options
  • Save joedski/0e0aee33a99ab2ab241a4b8bff4f2515 to your computer and use it in GitHub Desktop.
Save joedski/0e0aee33a99ab2ab241a4b8bff4f2515 to your computer and use it in GitHub Desktop.
Multiparameter Memoization: The whole shebang
// A simple enhancer to use on reselect's createSelectorCreator that makes it
// pass the number of selectors received as the first option (after the calculator)
// to the memoizer.
function passSelectorCountToMemoizer(baseCreateSelectorCreator) {
return function selectorPassingSelectorCreator(memoize, ...memoizeOptions) {
return function createActualSelector(...args) {
const calculator = args.pop();
const selectors = Array.isArray(args[0]) ? args[0] : args;
return baseCreateSelectorCreator(memoize, selectors.length, ...memoizeOptions)(selectors, calculator);
};
};
}
// Simple naive single-param single-memo.
function singleMemo(fn) {
let prevArg;
let value;
// This is more or less the default behavior of lodash.memoize.
return function singleMemoizedFn(arg) {
if (arg !== prevArg) {
prevArg = arg;
value = fn(arg);
}
return value;
};
}
function createApplier(calculator, memoizers, totalValueCount, currValues) {
const memoize = memoizers[currValues.length];
function applyNonFinalValue(value) {
const nextValues = [...currValues, value];
return createApplier(calculator, memoizers, totalValueCount, nextValues);
}
function applyFinalValue(value) {
return calculator(...currValues, value);
}
const applyValue = (
// If the next one applies the final value, that is it would bring values.length
// up to totalValueCount, use applyFinalValue. Otherwise, use
// applyNonFinalValue.
currValues.length >= totalValueCount - 1 ? applyFinalValue : applyNonFinalValue
);
return memoize(applyValue);
}
function createTieredMemoizer(options = {}) {
const {
// memoizers is an array of functions which accept a function and return
// a memoized version, usually by ensorceling it within a memoizer of some sort.
memoizers = [],
// This is what allows you to omit specifying a memoizer for every selector position.
defaultMemoizer = singleMemo,
} = options;
return function tieredMemoize(calculator, expectedArgCount) {
const reifiedMemoizers = memoizers.slice();
while (reifiedMemoizers.length < expectedArgCount) {
reifiedMemoizers.push(defaultMemoizer);
}
const applyValue = createApplier(calculator, reifiedMemoizers, expectedArgCount, []);
return function selector(...values) {
return values.reduce(
function recurApplication(nextApplyValue, nextValue) {
return nextApplyValue(nextValue);
},
applyValue
);
};
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment