通过功能性编程方式将道具从obj复制到obj

时间:2018-12-10 13:22:37

标签: javascript functional-programming copy

我有这些接口:

  export interface QueryObject {
    id: ID;
    path: string[];
    filters: Filters;
  }

  export interface Filters {
    state: string;
    localeID: string;
    role: string;
  }

并尝试提出一个功能编程解决方案,以便仅将tmp对象中存在的并具有值的属性复制到现有数据模型中。现在,..显然这是行不通的。 Filters将被完全覆盖,同时丢失属性localeIDrole

  let queryObject: QueryObject;
  let filters: Filters = { state: 'GB'}; // this obviously gives an error on Interface implementation
  queryObject.filters = filters;

现在,我正在获取原始对象,遍历该属性并用更新后的值覆盖它。

  const queryObject: QueryObject = _.cloneDeep(this.queryObject);
  queryObject.filters.state = state; // 'GB'
  this.portareService.update(queryObject, this.portareQuery.getActiveId());

使用Object.assign或传播...这样的解决方案来解决此问题将非常好:

{
  return ...createQueryObject, updatedQueryObject
}

我知道如何使用一个或多个循环使用函数,但是正在寻找一种功能编程方法。

1 个答案:

答案 0 :(得分:2)

您可以为concatQueryObject都实现Filters方法。在concat中,定义要使用的“合并逻辑”。 QueryObject在内部调用Filters'concat方法。

concat方法中,您可以使用传播语法或任何其他逻辑来确保创建新对象,并且不对任何内容进行突变。

通过添加empty构造函数,您可以轻松地开始在reduce或其他自动合并中使用那些串联器。

我发现this blog post on Semigroups by Tom Harding很有启发性。 This post about Monoids中包含有关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)
)