【问题标题】:logical optimisation using Ramda使用 Ramda 进行逻辑优化
【发布时间】:2021-06-25 21:10:49
【问题描述】:

我已经使用 Ramda 制作了一个解析功能,它就像一个魅力,但我很确定它可以被简化,不幸的是我正在努力克服这个优化

我想将这些值分组到一个具有“名称”且“y”是计数的结构中。例如,如果“待定”出现两次,并且在我的输入中出现“雇用”一次,那么我想得到你在输出中看到的内容

我写的函数

const aggResumeStatusTest = data =>
  reduce(
    (acc, elem) => {
      if (!isEmpty(acc)) {
        const indexInArray = findIndex(propEq('name', elem))(acc);
        if (indexInArray !== -1) {
          acc[indexInArray].y++;
        } else {
          acc.push({
            name: elem,
            y: 1,
          });
        }
        return acc;
      }

      acc.push({
        name: elem,
        y: 1,
      });

      return acc;
    },
    [],
    data,
  );

aggResumeStatusTest(['pending', 'pending', 'hired'])

输入

['pending', 'pending', 'hired']

输出

[ { name: 'pending', y: 2 }, { name: 'hired', y: 1 } ]

提前感谢您的帮助

【问题讨论】:

    标签: javascript ramda.js


    【解决方案1】:

    我不知道这是否算作优化,但您的代码可以简化。

    首先,R.groupBy 对所有这些字符串进行分组。所以你可以从:

    ['pending', 'pending', 'hired']
    

    收件人:

    [['pending', 'pending'], 'hired']
    

    不同的是,它返回的是一个对象,而我们只对它的值感兴趣,所以我们使用R.values

    那么你所要做的就是映射它并获取它的长度。

    所以最后你有:

    let countToObj = arr => ({name : R.head(arr), y: arr.length })
    R.map(countToObj, R.values(R.groupBy(R.identity, data)));
    

    let countToObj = arr => ({name : R.head(arr), y: arr.length })
    let aggResumeStatusTest = data => R.map(countToObj, R.values(R.groupBy(R.identity, data)));
    console.log(aggResumeStatusTest(['pending', 'pending', 'hired']));
    <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.21.0/ramda.min.js"></script>

    【讨论】:

    • 这仅适用于排序的输入,因为相邻的项目被传递给比较。
    • groupBy 按我认为你想要的方式工作。但countBy 更具语义性。
    • 我希望groupBy 带有数组(作为返回值),但我想没有。所以,我就用R.values
    • @MinusFour:是的,不存在,但应该存在。请随时为此提出Issue 甚至更好的Pull Request
    • 谢谢大家,这对我帮助很大
    【解决方案2】:

    这是一个用 ramda 很容易完成的任务, 请看R.countBy;

    const count = R.pipe(
      R.countBy(R.identity),
      R.toPairs,
      R.map(([name, y]) => ({ name, y })),
    )
    
    
    // ====
    
    const data = ['pending', 'pending', 'hired'];
    
    console.log(
      count(data),
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

    【讨论】:

    • 这里的only 问题(非常小)是输入被转换为字符串。因此,使用数字尝试此操作将返回字符串。并尝试使用混合 [1, "1", 2]1s 组合在一起,即使它们是不同的类型。 IMO,这不是问题,因为我希望输入只是字符串,但提到它以防万一。可以在 Map 中完成计数以保留类型。当然,如果需要,我怀疑绝大多数时间都不会。
    • 好吧,OP 很清楚string[] 输入,当然不同的输入类型需要重新设计此算法...
    • 正如我所说,这是一个非常小的问题。如果有人遇到这个并且碰巧想要对其他类型进行分组,那么更多的是对未来的说明。我相信答案是正确的,我没有任何异议。
    • 它也不适用于对象,但我想这也不是问题的一部分。
    • 当我发布非常相似的解决方案时,我还没有看到你的解决方案。伟大的思想,以及所有这些!我不确定在最后一步中我更喜欢你的 lambda 还是我的 zipObj,但非常相似,直到选择的名称!
    【解决方案3】:

    我喜欢将这些问题视为一系列转换,与pipecompose 结合在一起。

    const count = pipe (
      countBy (identity),
      toPairs,
      map (zipObj (['name', 'y']))
    )
    
    console .log (count (['pending', 'pending', 'hired']))
    <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
    <script> const {pipe, countBy, identity, toPairs, map, zipObj} = R       </script>

    这里我们首先使用countBy 转换为{pending: 2, hired: 1}。然后使用toPairs,我们生成[['pending', 2], ['hired', 1]]。最后,我们mapzipObj将这些数组转换成合适的对象,[{"name": "pending", "y": 2}, {"name": "hired", "y": 1}]

    【讨论】:

      【解决方案4】:

      HitmandsScott Sauyet 的答案非常好,我完全赞同他们。

      我想展示另一种实现方式。首先,使用纯 JavaScript,我们可以对 Map 进行计数,然后将该映射作为可迭代的 Array.from 转换为对象:

      const countBy = (mapping, input) => {
        const m = new Map();
        for (const item of input) {
          const key = mapping(item);
          m.set(key, (m.get(key) ?? 0) + 1);
        }
        return m.entries();
      };
      
      const identity = x => x;
      
      const count = input =>
        Array.from(
          countBy(identity, input), 
          ([name, y]) => ({name, y})
        );
      
      console.log(count(['pending', 'pending', 'hired']));
      .as-console-wrapper {
        max-height: 100% !important;
      }

      这可以转化为 Ramda 的说法:

      //custom implementation
      // (a → b) → [a] → [[b, Number]]
      const countBy = curry((mapping, input) => {
        const m = new Map();
        for (const item of input) {
          const key = mapping(item);
          m.set(key, (m.get(key) ?? 0) + 1);
        }
        return m.entries();
      });
      
      const arrayFrom = pipe(binary, flip, curry) (Array.from);
      
      const count = pipe (
        countBy(identity),
        arrayFrom(zipObj(["name", "y"]))
      );
      
      console.log(count(['pending', 'pending', 'hired']));
      .as-console-wrapper {
        max-height: 100% !important;
      }
      <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
      <script> const {pipe, curry, identity, flip, zipObj, binary} = R; </script>

      这样,您不仅可以对字符串进行分组,还可以对任何数据类型进行分组,因为地图将保留放入的任何数据:

      //custom implementation
      // (a → b) → [a] → [[b, Number]]
      const countBy = curry((mapping, input) => {
        const m = new Map();
        for (const item of input) {
          const key = mapping(item);
          m.set(key, (m.get(key) ?? 0) + 1);
        }
        return m.entries();
      });
      
      const arrayFrom = pipe(binary, flip, curry) (Array.from);
      
      const count = pipe (
        countBy(identity),
        arrayFrom(zipObj(["name", "y"]))
      );
      
      console.log(" --- numbers --- ");
      console.log(count([1, 2, "1", 2]));
      
      
      console.log(" --- objects --- ");
      const o1 = {foo: 1};
      const o2 = {foo: 2};
      const o3 = {foo: 1};
      console.log(count([o1, o2, o3, o2]));
      .as-console-wrapper {
        max-height: 100% !important;
      }
      <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
      <script> const {pipe, curry, identity, flip, zipObj, binary} = R; </script>

      同样,这个实现可能是不需要的。但在极少数情况下可能会引起兴趣。

      【讨论】:

      • 注意:我的 Hindley-Milner 表示法技能很弱。我知道[[b, Number]] 不是正确的类型,但我不确定如何表达“[b, Number] 元组的可迭代”。我愿意接受更正和建议。
      • Ramda 团队一直在尝试扩展 H-M 来处理 JS 结构,而我对结果从来都不是很满意。这看起来和我能想到的一样好。
      • 我喜欢基于MapcountBy。但我很好奇arrayFrom 的基本原理。 ISTM 认为将Array.from 传送到map (zipObj (["name", "y"])) 会更容易。我错过了什么吗?
      • @ScottSauyet 我想我只是想创建一个快捷方式而不是创建一个中间数组,而是直接使用迭代器并在过程中映射它。你说得对,pipe ( countBy(identity), Array.from, map(zipObj(["name", "y"])) ); 有相同的结果。没有比我的偏好更深刻的意义了。我也认识到获得arrayFrom 的转换有点难看。 TBH,我通常只使用a generator to map over an iterable。但这里是多余的,因为Array.from(map(f, xs))Array.from(xs, f) 相同。
      猜你喜欢
      • 2015-01-31
      • 1970-01-01
      • 1970-01-01
      • 2012-04-21
      • 2012-08-10
      • 1970-01-01
      • 1970-01-01
      • 2016-09-11
      • 1970-01-01
      相关资源
      最近更新 更多