Lodash中reduce函数与includes或orderBy迭代器的用例

3
从lodash文档中reduce的说明:
_.reduce(collection, [iteratee=_.identity], [accumulator])

Reduces collection to a value which is the accumulated result of running each element in collection thru iteratee, where each successive invocation is supplied the return value of the previous. If accumulator is not given, the first element of collection is used as the initial value. The iteratee is invoked with four arguments: (accumulator, value, index|key, collection).

Many lodash methods are guarded to work as iteratees for methods like _.reduce, _.reduceRight, and _.transform.

The guarded methods are: assign, defaults, defaultsDeep, includes, merge, orderBy, and sortBy

我能看出如何将reduce应用于大多数其他方法,因为它们大致遵循方法签名。例如,assigndefaults的形式为(非正式TypeScript伪代码):
_.assign and _.defaults: (object: object, sources: collection) => object
                          ^ accumulator   ^ value                 ^ same type as accumulator

同样,sortBy 也有意义:

_.sortBy: (collection: object, iteratees: sort_fn[]) => object
           ^ accumulator       ^ value                  ^ same type as accumulator

我可以看到这些的有用用例,例如:

console.log('reduce',
    _.reduce([{a: 2, b: 4}, [123, 52], {a: 5}], _.assign),
    _.reduce([{a: 2, b: 4}, [123, 52], {a: 5}], _.defaults),
    _.reduce([[e => e.a], [e => e.b]], _.sortBy,
      [{a: 3, b: 2}, {a: 3, b: 3}, {a: 2, b: 15}, {a: -2, b: 3}])
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>


然而,我不确定如何在使用 orderByincludes 时应用此方法。也就是说,

_.orderBy: (collection: object, iteratees: sort_fn[], orders: ('asc' | 'desc')[]) => object
            ^ accumulator       ^ value               ^ index???                     ^ same type as accumulator

查看源代码并跟随一些方法调用,似乎这仍然有效,尽管第三个参数是'asc' | 'desc'值的数组(由于保护,它会特殊处理)。最终,通过保护,它似乎将所有order默认为asc,因此它的作用就像sortBy

console.log('reduce',
    _.reduce([[e => e.a], [e => e.b]], _.orderBy,
      [{a: 3, b: 2}, {a: 3, b: 3}, {a: 2, b: 15}, {a: -2, b: 3}])
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

因此,如果以这种方式使用,保护似乎并不是很有用,只是使其成为sortBy的替代品,有效地使orders参数无用。 对于includes,我甚至不确定如何将其用作reducer函数,因为它的返回类型(布尔值)与其第一个参数不同,例如:
_.includes: (collection: object, value: any) => boolean
             ^ accumulator       ^ value        ^ not same type as accumulator?
我的问题是:是否有任何实用的方法可以将_.orderBy_.includes用作_.reduce_.transform迭代器?如果不能,为什么lodash文档中列出了这样一种受保护的函数,可以以这种方式使用? 抱歉,如果这听起来有点像两个问题,但我认为它们足够相关,应该放在一起。

有一个非常奇怪的用例我可以想到 _.includes - 你可以这样使用它:_.reduce([[1, 2, 3], 3], _.includes),它检查第一个索引是否包含第二个索引。所以,它类似于 _.includes.apply(null, [[1, 2, 3], 3])。然而,我无法想出为什么你会想这样调用它。 - VLAZ
1个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
3

简而言之(完整内容请见下文)

我猜测在使用_.reduce时,并不需要对_.includes进行保护。相反,这可能是多年前的失误或者未能更好地澄清文档中那一部分。

添加保护参数的意图是使其在部分应用时可以与_.every_.some等函数一起使用。

就像这样:

_.every([1, 2], _.partial(_.includes, [1, 2, 3, 4])) // => true
_.every([1, 5], _.partial(_.includes, [1, 2, 3, 4])) // => false

全文

我试图用更简洁的方式替换代码中使用了一些丑陋的_.filter_.clone!_.includes的部分。

通常情况下,我会扫描lodash文档并尝试激发我的创造力。

然后我在文档中找到了你提到的那行代码,在尝试了一些_.reduce_.includes之后,得出了和你相同的结论:这不合理。

我觉得可能是我错了,于是我谷歌搜索希望能够找到一些聪明的例子,最终来到了这里。

看到我不是一个人这么想,感觉好多了,但由于还没有人回答这个问题(至少在我写这篇文章时没有),所以我仍然无法解决我脑海中的问题。

因此,我去了最后一个可以尝试挖掘一些信息的地方:源代码

经过一番逐个指责的过程,我终于找到了引入该变化的提交

其中最有趣的部分是在测试中添加了一个_.includes部分:

      strictEqual(_.includes([-0], 0), true);
    });

+   test('should work as an iteratee for methods like `_.reduce`', 1, function() {
+     var array1 = [1, 2, 3],
+         array2 = [2, 3, 1];
+
+     ok(_.every(array1, _.partial(_.includes, array2)));
+   });
+
    test('should be aliased', 2, function() {
      strictEqual(_.contains, _.includes);
      strictEqual(_.include, _.includes);

仔细观察,你会发现它根本没有测试与_.reduce相关的任何内容!

因此,接下来我去了测试套件,并尝试找到任何与该功能相关的额外测试,可能是自从那个提交以来添加的,有趣的是,我找到了完全相反的东西!

显然,有一天整个测试套件发生了大变化,以替换使用lodash函数而不是实际测试的函数的所有位置,使用第二个更老(且稳定)的lodash实例,可能是为了避免一个bug,使测试无法捕获到这个bug,进而导致测试无法捕获到这个bug......

那个变化中,有人被迫注意测试套件中每个_的细节,可能意识到该特定测试的描述根本没有意义。

于是他们进行了更改:

       assert.strictEqual(_.includes([0], -0), true);
     });

-    QUnit.test('should work as an iteratee for methods like `_.reduce`', function(assert) {
+    QUnit.test('should work as an iteratee for methods like `_.every`', function(assert) {
       assert.expect(1);

       var array1 = [1, 2, 3],
           array2 = [2, 3, 1];

-      assert.ok(_.every(array1, _.partial(_.includes, array2)));
+      assert.ok(lodashStable.every(array1, lodashStable.partial(_.includes, array2)));
     });
   }(1, 2, 3, 4));
最后,我尝试阅读了添加在 _.includes 函数中的保护之前提交的 _.every 函数的源代码,但是这并没有给我带来期望中的快速答案,所以我从 CDN 获取了版本 3.5.0(这是最后一个在 _.includes 函数中没有进行更改的版本),将其直接包含在空页面的

那是疯狂的研究。 - Rainb
@Rainb 哈哈,是的。我有这样一种特质,既是福也是祸,就是我必须要理解事物的运作方式,包括它们的内部结构。这也适用于想法(意思是我感到迫切需要理解产生该想法的思维过程和决策),但更重要的是,在像这种“为什么这玩意儿不像我想象中那样工作”的情况下,这种特质尤其适用 xD - Doodad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,