Я думаю, что вы ищете, чтобы возложить ответственность за это поведение не на то место. Если вы хотите, чтобы у ваших конвейерных функций было одно поведение с определенными данными и другое поведение с другими данными (или в данном случае с отсутствующими данными), то эти отдельные функции должны обрабатывать это, а не функция конвейера, которая их переносит.
Но, как указал Ори Дрори, вы можете написать декоратор функций, чтобы это произошло.
Вот одно из предложений:
// Dummy implementations
const filterWithRadius = (lat, lng, radius, resources) =>
({...resources, radiusFilter: `${lat}-${lng}-${radius}`})
const filterWithOptions = (opts, val, resources) =>
({...resources, [`optsFilter-${opts}`]: val})
// Test function (to be used in pipelines, but more general)
const ifNonNil = (fn) => (...args) => any(isNil, args)
? identity
: (data) => fn (...[...args, data])
// alternately, for variadic result : (...newArgs) => fn (...[...args, ...newArgs])
// Pipeline call
const getUpdatedResources = (
{lat, lng, radius, filterOptions, keyword, tagOptions, tag}
) => pipe (
ifNonNil (filterWithRadius) (lat, lng, radius),
ifNonNil (filterWithOptions) (filterOptions, keyword),
ifNonNil (filterWithOptions) (tagOptions, tag)
)
// Test data
const resources = {foo: 'bar'}
const query1 = {
lat: 48.8584, lng: 2.2945, radius: 10,
filterOptions: 'baz', keyword: 'qux',
tagOptions: 'grault', tag: 'corge'
}
const query2 = {
lat: 48.8584, lng: 2.2945, radius: 10,
tagOptions: 'grault', tag: 'corge'
}
const query3 = {
lat: 48.8584, lng: 2.2945, radius: 10,
filterOptions: 'baz', keyword: 'qux',
}
const query4 = {
filterOptions: 'baz', keyword: 'qux',
tagOptions: 'grault', tag: 'corge'
}
const query5 = {
lat: 48.8584/*, lng: 2.2945*/, radius: 10,
filterOptions: 'baz', keyword: 'qux',
tagOptions: 'grault', tag: 'corge'
}
const query6 = {}
// Demo
console .log (getUpdatedResources (query1) (resources))
console .log (getUpdatedResources (query2) (resources))
console .log (getUpdatedResources (query3) (resources))
console .log (getUpdatedResources (query4) (resources))
console .log (getUpdatedResources (query5) (resources))
console .log (getUpdatedResources (query6) (resources))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script> const {any, isNil, pipe, identity} = R </script>
Мы начнем с фиктивных реализаций ваших filter*
функций, которые просто добавляют свойство во входной объект.
Важная функция здесь ifNotNil
. Он принимает функцию n
аргументов, возвращая функцию n - 1
аргументов, которая при вызове проверяет, является ли какой-либо из этих аргументов nil
. Если они есть, он возвращает функцию идентификации; в противном случае он возвращает функцию с одним аргументом, которая, в свою очередь, вызывает исходную функцию с аргументами n - 1
и этот последний.
Мы используем это для построения конвейера, который будет возвращен функцией, которая принимает требуемые переменные (здесь наивно декструктурированы из потенциального объекта запроса.) Эта функция вызывается путем передачи запроса, а затем фактических данных, подлежащих преобразованию.
В примерах показаны различные комбинации параметров, включенных и исключенных.
Это делает предположение, что ваши функции не каррированы, что, скажем, filterWithRadius
выглядит как (lat, lng, radius, resources) => ...
Если они каррированы мы могли бы написать это вместо этого:
const ifNonNil = (fn) => (...args) => any(isNil, args)
? identity
: reduce ((f, arg) => f(arg), fn, args)
используется с
const filterWithRadius = (lat) => (lng) => (radius) => (resources) =>
({...resources, radiusFilter: `${lat}-${lng}-${radius}`})
, но все еще вызывается в конвейере как
pipe (
ifNonNil (filterWithRadius) (lat, lng, radius),
// ...
)
Вы можете даже смешивать и сопоставлять версии с карри и без карри в одном и том же конвейере, хотя я ожидаю, что это добавит путаницы.