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

时间:2017-02-01 21:25:23

标签: javascript ramda.js

我们正在尝试利用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减少数组,并累积一个包含该id的所有“failedReasons”的子“failedReason”数组。我们希望一些Ramda魔术可以做到这一点,但到目前为止我们还没有找到一个好的方法。任何想法,将不胜感激。

3 个答案:

答案 0 :(得分:3)

我无法在手机上轻松测试,但这样的事情应该有效:

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 上看到这一点。

答案 1 :(得分:2)

您可以定义满足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更好的名字。 ;)

答案 2 :(得分:1)

为了好玩,主要是为了探索Ramda的优点,我试图想出一个&#34;一个衬垫&#34;在 plain ES6 中进行相同的数据转换......我现在完全理解Scott's answer的简单性:D

我认为我会分享我的结果,因为它很好地说明了清晰的API在可读性方面可以做些什么。管道mapflattenuniq链更容易掌握......

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

&#13;
&#13;
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);
&#13;
&#13;
&#13;

如果在东方,MapIteratorSetIterator s会有.map甚至是.toArray方法,那么代码就会有点过了清洁剂。