代码之家  ›  专栏  ›  技术社区  ›  Armeen Moon

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

  •  4
  • Armeen Moon  · 技术社区  · 8 年前

    如何使用FP将两个函数组合成1 compose() 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 通过创建的函数 .

    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 回复  |  直到 7 年前
        1
  •  4
  •   Nima Hakimi    8 年前

    const ungroupAndFlatten = compose(flatten, ungroup)
    

    切换“解组”和“展平”,一切都会正常工作

        2
  •  2
  •   Roman Nime Cloud    7 年前

    功能组成

    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)
      )
    • 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)))
      

    我们的目标是 ungroup flatten groupBy

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

    但这行不通。我们只能用一个参数组合函数。解决方法是重写 到a 咖喱

    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)
    )
        3
  •  0
  •   T4rk1n    8 年前

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