【问题标题】:Passing _.groupBy to _.partialRight seems to give incorrect results将 _.groupBy 传递给 _.partialRight 似乎给出了不正确的结果
【发布时间】:2022-01-16 07:51:56
【问题描述】:

这是一个 JavaScript 对象,

const obj = {a: [{ id: 1 }, {id: 1}, {id: 2}, {id: 3}], b: [{ id: 4 }, {id: 5}, {id: 5}, {id: 6}] };

这是一个代码,它在.id 两个数组ojb.aobj.b 中的每一个中正确地对项目进行分组,

const res1 = _.map(obj, x => _.groupBy(x, 'id'));

结果是

[
 {
  1: [{id: 1}, {id: 1}],
  2: [{id: 2}],
  3: [{id: 3}]
 },
 {
  4: [{id: 4}],
  5: [{id: 5}, {id: 5}],
  6: [{id: 6}]
 }
]

然而,lambda 实际上只是 _.groupBy 对其第二个参数的部分应用,第二个参数设置为 'id',所以我认为这样的事情应该可以工作,

const res2 = _.map(obj, _.partialRight(_.groupBy, 'id'));

或者至少是这样的

const res2 = _.map(obj, _.partialRight(_.groupBy, x => x.id));

但是,它们都不起作用,都导致了这个对象:

[
 {
   undefined: [{id: 1}, {id: 1}, {id: 2}, {id: 3}]
 },
 {
   undefined: [{id: 4}, {id: 5}, {id: 5}, {id: 6}]
 }
]

这是为什么呢?这是lodash 中的错误吗?还是因为 JavaScript 的工作原理?在后一种情况下,发生了什么?


我找到了一个现有的question + self-answer,它提供了使上述代码工作的解决方案:

const res2 = _.map(obj, _.ary(_.partialRight(_.groupBy, 'id'), 1));

但是我的部分问题仍未得到解答:为什么我需要使用_.ary?为什么我的初始尝试不起作用?

【问题讨论】:

    标签: javascript functional-programming lodash partial-application


    【解决方案1】:

    _.partialRight 方法仍然可以接受比新函数预期更多的参数。如果一个函数有两个参数,有一个或两个部分应用,那么任何 额外 参数都会有效地“消除”部分应用的参数:

    function print(a, b) {
      console.log(a, b);
    }
    
    const f = _.partialRight(print, "world");
    const g = _.partialRight(print, "hello", "world");
    
    f("hi");                    // hi world
    g();                        // hello world
    
    f("hi", "universe");        // hi universe
    g("greetings");             // greetings world
    g("greetings", "universe"); // greetings universe
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

    这是因为_.partialRight 有效地添加到arguments 对象的末尾:

    function print(a, b) {
      console.log(...arguments);
    }
    
    const f = _.partialRight(print, "world");
    const g = _.partialRight(print, "hello", "world");
    
    f("hi");                    // hi world
    g();                        // hello world
    
    f("hi", "universe");        // hi universe world
    g("greetings");             // greetings hello world
    g("greetings", "universe"); // greetings universe hello world
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

    因此_.partialRight 构造的函数很容易受到the same problem that passing parseInt as callback has 的影响 - 可以传入更多参数并且 传入,因为_.map 的回调总是传递元素,索引和数组。因此,即使_.partialRight(_.groupBy, 'id') 应该将第二个参数设置为'id',但当_.mapcallback(item, index, array) 调用该函数时,它会变成第四个参数。实际上,被执行的回调是

    (item, index, array) => _.groupBy(item, index, array, 'id')
    

    这就是为什么用_.ary(fn, 1) 或直接用_.unary() 压制arity 起作用的原因 - 在这种情况下,来自_.map() 的额外参数将被丢弃,并且只会处理第一个参数:

    function print(a, b) {
      console.log(a, b);
    }
    
    const f = _.unary(_.partialRight(print, "world"));
    
    f("hi");                    // hi world
    f("hi", "universe");        // hi world
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

    作为记录,如果您更喜欢更实用的风格和point-free style,那么您可以使用 Lodash 的Lodash FP 发行版,这使得这更容易。所有导出的函数都被柯里化了,参数也被改变了,所以数据总是最后的。这使您可以更轻松地为给定数据构建处理:

    const obj = {a: [{ id: 1 }, {id: 1}, {id: 2}, {id: 3}], b: [{ id: 4 }, {id: 5}, {id: 5}, {id: 6}] };
    
    const process = _.map(_.groupBy("id"));
    
    console.log(process(obj));
    .as-console-wrapper { max-height: 100% !important; }
    <script src="https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)"></script>

    【讨论】:

      猜你喜欢
      • 2022-08-13
      • 1970-01-01
      • 2016-07-11
      • 2019-08-04
      • 2015-08-25
      • 2012-08-22
      • 2014-04-08
      • 1970-01-01
      • 2012-08-27
      相关资源
      最近更新 更多