Вы можете реализовать метод concat
для QueryObject
и Filters
.В concat
вы определяете, какую «логику слияния» вы хотите использовать.QueryObject
вызывает внутренний метод Filters
'concat.
Внутри методов concat
вы можете использовать синтаксис распространения или любую другую логику, чтобы гарантировать создание новых объектов и ничего не мутировать.
Добавив конструктор empty
, вы можете легко начать использовать эти конкатенаторы внутри reduce
или другого автоматического слияния.
Я нашел этот пост в блоге о полугруппах Тома Хардинга супер вдохновляющим. Этот пост о моноидах содержит некоторую информацию о empty
части.
const QueryObject = ({id = null, path = null, filters = Filters.empty() })=> ({
id,
path,
filters,
concat: other => QueryObject({
id: other.id || id,
path: other.path || path,
filters: filters.concat(other.filters)
}),
toString: () => `QueryObject(${id}, ${path}, ${filters.toString()})`
});
QueryObject.empty = () => QueryObject({});
QueryObject.merge = (x, y) => x.concat(y);
const Filters = ({ state = null, localeID = null, role = null }) => ({
state,
localeID,
role,
concat: other => Filters({
state: other.state || state,
localeID: other.localeID || localeID,
role: other.role || role
}),
toString: () => `Filters(${state}, ${localeID}, ${role})`
});
Filters.empty = () => Filters({});
Filters.merge = (x, y) => x.concat(y);
const userFilter = Filters({ role: "User" });
const gbFilter = Filters({ localeID: "GB" });
const filterSettings = [userFilter, gbFilter];
const mergedFilter = filterSettings.reduce(Filters.merge, Filters.empty());
console.log(
"Merged Filter:",
mergedFilter.toString()
);
// Some base query
const accountQuery = QueryObject({ id: "CUSTOM_Q_1", path: "/accounts" });
// Derived queries
const userQuery = accountQuery.concat(QueryObject({ filters: userFilter }));
const gbQuery = accountQuery.concat(QueryObject({ filters: gbFilter }));
console.log(
"User Query:",
userQuery.toString()
);
console.log(
"Brittish Users Query",
userQuery.concat(gbQuery).toString()
);
Редактировать: Конечно, без "теории", есть и более общее:
const uniques = xs => Array.from(new Set(xs));
const nullMergeStrategy = (obj1, obj2) =>
uniques(
Object.keys(obj1)
.concat(Object.keys(obj2))
).reduce(
(acc, k) => Object.assign(acc, { [k]: obj2[k] || obj1[k] }),
{}
);
const Filter = ({ state = null, localeID = null, role = null }) =>
({ state, localeID, role });
const userFilter = Filter({ role: "User" });
const gbFilter = Filter({ localeID: "GB" });
console.log(
nullMergeStrategy(userFilter, gbFilter)
)