Ramda 建议从稍微嵌套的数组中删除重复项

Ramda recommendation for removing duplicates from a slightly nested array

我们正在尝试利用 Ramda 来避免一些暴力编程。我们有一个对象数组,看起来像这样:

[
{id: "001", failedReason: [1000]},
{id: "001", failedReason: [1001]},
{id: "001", failedReason: [1002]},
{id: "001", failedReason: [1000]},
{id: "001", failedReason: [1000, 1003]},
{id: "002", failedReason: [1000]}
]

我们想将其转换为如下所示:

[
{id: "001", failedReason: [1000, 1001, 1002, 1003]},
{id: "002", failedReason: [1000]}
]

本质上它是根据id减少数组,并累积一个子"failedReason"数组,其中包含该id的所有"failedReasons"。我们希望一些 Ramda 魔法可以做到这一点,但到目前为止我们还没有找到一个好的方法。任何想法,将不胜感激。

我不能轻易地在我的 phone 上测试它,但像这样的东西应该可以工作:

pipe(
  groupBy(prop('id')), 
  map(pluck('failedReason')),
  map(flatten),
  map(uniq)
)

更新

我刚开始在计算机上查看此内容,发现输出结果与您想要的不完全一样。再添加两个步骤即可解决问题:

pipe(
  groupBy(prop('id')), 
  map(pluck('failedReason')),
  map(flatten),
  map(uniq),
  toPairs,
  map(zipObj(['id', 'failedReason']))
)

您可以在 Ramda REPL.

上看到实际效果

您可以定义满足 Monoid 要求的包装器类型。然后,您可以简单地使用 R.concat 来组合以下类型的值:

//  Thing :: { id :: String, failedReason :: Array String } -> Thing
function Thing(record) {
  if (!(this instanceof Thing)) return new Thing(record);
  this.value = {id: record.id, failedReason: R.uniq(record.failedReason)};
}

//  Thing.id :: Thing -> String
Thing.id = function(thing) {
  return thing.value.id;
};

//  Thing.failedReason :: Thing -> Array String
Thing.failedReason = function(thing) {
  return thing.value.failedReason;
};

//  Thing.empty :: () -> Thing
Thing.empty = function() {
  return Thing({id: '', failedReason: []});
};

//  Thing#concat :: Thing ~> Thing -> Thing
Thing.prototype.concat = function(other) {
  return Thing({
    id: Thing.id(this) || Thing.id(other),
    failedReason: R.concat(Thing.failedReason(this), Thing.failedReason(other))
  });
};

//  f :: Array { id :: String, failedReason :: Array String }
//    -> Array { id :: String, failedReason :: Array String }
var f =
R.pipe(R.map(Thing),
       R.groupBy(Thing.id),
       R.map(R.reduce(R.concat, Thing.empty())),
       R.map(R.prop('value')),
       R.values);

f([
  {id: '001', failedReason: [1000]},
  {id: '001', failedReason: [1001]},
  {id: '001', failedReason: [1002]},
  {id: '001', failedReason: [1000]},
  {id: '001', failedReason: [1000, 1003]},
  {id: '002', failedReason: [1000]}
]);
// => [{"id": "001", "failedReason": [1000, 1001, 1002, 1003]},
//     {"id": "002", "failedReason": [1000]}]

我相信你可以给这个类型起一个比 Thing 更好的名字。 ;)

为了好玩,主要是为了探索 Ramda 的优势,我试图想出一个 "one liner" 在 plain ES6 中做同样的数据转换。 . 我现在完全理解 的简单性 :D

我想我应该分享我的结果,因为它很好地说明了清晰的 API 在可读性方面的作用。管道 maps、flattenuniq 的链更容易掌握...

我使用 Map 进行分组,使用 Set 过滤重复项 failedReason

const data = [ {id: "001", failedReason: [1000]}, {id: "001", failedReason: [1001]},  {id: "001", failedReason: [1002]}, {id: "001", failedReason: [1000]}, {id: "001", failedReason: [1000, 1003]}, {id: "002", failedReason: [1000]} ];

const converted = Array.from(data
  .reduce((map, d) => map.set(
      d.id, (map.get(d.id) || []).concat(d.failedReason)
    ), new Map())
  .entries())
  .map(e => ({ id: e[0], failedReason: Array.from(new Set(e[1])) }));
  
console.log(converted);

如果 MapIteratorSetIterator 至少有一个 .map 甚至 .toArray 方法,代码会更简洁一些。