如何使用FP' s compose()javascript将两个函数组合成1个?

时间:2017-07-11 18:50:37

标签: javascript function lambda functional-programming function-composition

如何使用FP compose()将两个函数组合成1 这里有实时代码:https://repl.it/JXMl/1

我有3个纯函数:

// groups By some unique key
const groupBy = function(xs, key) {
  return xs.reduce(function(rv, x) {
    (rv[x[key]] = rv[x[key]] || []).push(x);
    return rv;
  }, {});
};

// ungroups the group by
const ungroup = (obj) => {
  return Object.keys(obj)
               .map(x => obj[x]);
};

// flatten array
const flatten = (arrs) => {
  return arrs.reduce((acc, item) => acc.concat(item), [])
}

功能实用程序由Functional Jargon

组成函数
const compose = (f, g) => (a) => f(g(a))

最后,我希望通过ungroupAndFlatten创建一个compose()函数。

遵循:

const ungroupAndFlatten = compose(ungroup, flatten) // Usage doesn't work.
console.log(ungroupAndFlatten(obj)) 

示例代码:

const arrs = [
  {name: 'abc', effectiveDate: "2016-01-01T00:00:00+00:00"},
  {name: 'abcd', effectiveDate: "2016-02-01T00:00:00+00:00"},
  {name: 'abcd', effectiveDate: "2016-09-01T00:00:00+00:00"},
  {name: 'abc', effectiveDate: "2016-04-01T00:00:00+00:00"},
  {name: 'abc', effectiveDate: "2016-05-01T00:00:00+00:00"},
]; 

const groupedByName = groupBy(arrs, 'name');

// Example Output
//
// var obj = {
//    abc: [
//      { name: 'abc', effectiveDate: '2016-01-01T00:00:00+00:00' },
//      { name: 'abc', effectiveDate: '2016-04-01T00:00:00+00:00' },
//      { name: 'abc', effectiveDate: '2016-05-01T00:00:00+00:00' }
//    ],
//    abcd: [ 
//      { name: 'abcd', effectiveDate: '2016-02-01T00:00:00+00:00' },
//      { name: 'abcd', effectiveDate: '2016-09-01T00:00:00+00:00' }
//    ]
//  }

const ungroupAndFlatten = compose(ungroup, flatten) // Usage doesn't work.
console.log(ungroupAndFlatten(groupedByName)) 

// Output:
//  var arrs = [
//    {name: 'abc', effectiveDate: "2016-01-01T00:00:00+00:00"},
//    {name: 'abcd', effectiveDate: "2016-02-01T00:00:00+00:00"},
//    {name: 'abcd', effectiveDate: "2016-09-01T00:00:00+00:00"},
//    {name: 'abc', effectiveDate: "2016-04-01T00:00:00+00:00"},
//    {name: 'abc', effectiveDate: "2016-05-01T00:00:00+00:00"},
//  ];

3 个答案:

答案 0 :(得分:4)

我相信你犯了一个简单的错误,你将ungroup设置为你的f函数,而它是你的g函数:

const ungroupAndFlatten = compose(flatten, ungroup)

切换取消组合并展平,一切都会正常工作

答案 1 :(得分:2)

功能组合

函数compose的评估顺序从从右到左

/*
 * 1. take x
 * 2. execute g(x)
 * 3. take the return value of g(x) and execute f with it
*/
const compose = (f, g) => x => f(g(x))

另一个名为pipe的函数从从左到右评估

const pipe = (g, f) => x => f(g(x))

您的代码

正如@Nima Hakimi已经提到的,你已经颠倒了参数顺序。

为了解决你的问题,我们可以

  • 切换参数的顺序

    const ungroupAndFlatten = compose(
        flatten,
        ungroup
    )
    

    const compose = (f, g) => x => 
        f(g(x))
    
    const ungroup = obj =>
        Object.keys(obj)
            .map(x => obj[x]);
    
    const flatten = arrs =>
        arrs.reduce((acc, item) => acc.concat(item), [])
    
    const groupedByName = {
        abc: [
            { name: 'abc', effectiveDate: '2016-01-01T00:00:00+00:00' },
            { name: 'abc', effectiveDate: '2016-04-01T00:00:00+00:00' },
            { name: 'abc', effectiveDate: '2016-05-01T00:00:00+00:00' }
        ],
        abcd: [
            { name: 'abcd', effectiveDate: '2016-02-01T00:00:00+00:00' },
            { name: 'abcd', effectiveDate: '2016-09-01T00:00:00+00:00' }
        ]
    }
    
    const ungroupAndFlatten = compose(
        flatten,
        ungroup
    )
    
    console.log(
      ungroupAndFlatten(groupedByName)
    )

  • 或使用pipe

    const ungroupAndFlatten = pipe(
        ungroup,
        flatten
    )
    

    const pipe = (g, f) => x => 
        f(g(x))
    
    const ungroup = obj =>
        Object.keys(obj)
            .map(x => obj[x]);
    
    const flatten = arrs =>
        arrs.reduce((acc, item) => acc.concat(item), [])
    
    const groupedByName = {
        abc: [
            { name: 'abc', effectiveDate: '2016-01-01T00:00:00+00:00' },
            { name: 'abc', effectiveDate: '2016-04-01T00:00:00+00:00' },
            { name: 'abc', effectiveDate: '2016-05-01T00:00:00+00:00' }
        ],
        abcd: [
            { name: 'abcd', effectiveDate: '2016-02-01T00:00:00+00:00' },
            { name: 'abcd', effectiveDate: '2016-09-01T00:00:00+00:00' }
        ]
    }
    
    const ungroupAndFlatten = pipe(
        ungroup,
        flatten
    )
    
    console.log(
      ungroupAndFlatten(groupedByName)
    )

具有n参数的函数组合

我们再次

  • 撰写
    const compose = (...fns) => fns.reduceRight((f, g) => (...args) => 
        g(f(...args)))
    
  • const pipe = (...fns) => fns.reduce((f, g) => (...args) => 
        g(f(...args)))
    

我们的目标是同时撰写ungroupflatten函数groupBy。 我们可以试试

const groupByNameAndFlattenAndUngroup = compose(
    flatten,
    ungroup,
    groupBy('name', x)  // <-- this is our problem
)

但这不起作用。我们只能用一个参数组合函数。解决方案是将groupBy重写为 curried 版本:

const groupBy = xs => key =>
xs.reduce((rv, x) => {
    (rv[x[key]] = rv[x[key]] || []).push(x)
    return rv
}, {})

const groupByNameAndFlattenAndUngroup = (key, xs) => compose(
    flatten,
    ungroup,
    groupBy ('name')
) (xs)

但是这个解决方案可以更短。如果我们将参数的顺序从groupBy切换到groupBy = key => xs => {/*..*/},我们可以这样做:

const groupBy = key => xs =>
    xs.reduce((rv, x) => {
        (rv[x[key]] = rv[x[key]] || []).push(x)
        return rv
    }, {})

const groupByNameAndFlattenAndUngroup = compose(
    flatten,
    ungroup,
    groupBy ('name')
)

工作示例

const arrs = [
  {name: 'abc', effectiveDate: "2016-01-01T00:00:00+00:00"},
  {name: 'abcd', effectiveDate: "2016-02-01T00:00:00+00:00"},
  {name: 'abcd', effectiveDate: "2016-09-01T00:00:00+00:00"},
  {name: 'abc', effectiveDate: "2016-04-01T00:00:00+00:00"},
  {name: 'abc', effectiveDate: "2016-05-01T00:00:00+00:00"},
]

const compose = (...fns) => fns.reduceRight((f, g) => (...args) => 
    g(f(...args)))
    
const ungroup = obj =>
    Object.keys(obj)
        .map(x => obj[x]);

const flatten = arrs =>
    arrs.reduce((acc, item) => acc.concat(item), [])
    
const groupBy = key => xs =>
  xs.reduce((rv, x) => {
      (rv[x[key]] = rv[x[key]] || []).push(x)
      return rv
  }, {})
  
const groupByName = groupBy ('name')
const groupByEffectiveDate = groupBy ('effectiveDate')
  
const groupByNameAndFlattenAndUngroup = compose(
    flatten,
    ungroup,
    groupByName
)

const groupBygroupByEffectiveDateAndFlattenAndUngroup = compose(
    flatten,
    ungroup,
    groupByEffectiveDate
)

console.log(
  groupByNameAndFlattenAndUngroup (arrs)
)

console.log(
  groupBygroupByEffectiveDateAndFlattenAndUngroup (arrs)
)

答案 2 :(得分:0)

您可以在单个函数中取消组合和展平。

const ungroupAndFlatten = (obj) => Object.keys(obj).reduce((a, k) => {obj[k].forEach(e => a.push(e)); return a}, [])