【问题标题】:ramda.js: obtain a set of duplicates from an array of objects using specific propertiesramda.js:使用特定属性从对象数组中获取一组重复项
【发布时间】:2019-04-21 10:25:31
【问题描述】:

给定这个数组,包含 javascript 对象 (json):
每个对象都有一个bproperty 和一个u 属性,

(每个都包含我在本练习中不关心的其他属性)。

[
    { "b": "A", "u": "F", ... },
    { "b": "M", "u": "T", ... },
    { "b": "A", "u": "F", ... },
    { "b": "M", "u": "T", ... },
    { "b": "M", "u": "T", ... },
    { "b": "X", "u": "Y", ... },
    { "b": "X", "u": "G", ... },
]

我想使用 ramda 来查找所有重复项的集合。 结果应该是这样的。

[ 
    { "b": "A", "u":"F" },
    { "b": "M", "u":"T" } 
]

这两个条目有重复,它们分别在原始列表中重复了 2 次和 3 次。

编辑

我找到了一个使用underscore 的解决方案,它保留了原始数组元素,并将它们完美地拆分为单项和重复项。我更喜欢 ramda.js,并且下划线不只是给出一组重复项 - 根据问题,所以我将问题保持开放,直到有人可以使用 ramda 回答。我将继续使用下划线,直到问题得到解答。

我有一个 repl,它可以找到唯一值...作为开始...

【问题讨论】:

  • 您想要完整的副本还是只想要在bu 上匹配的副本?
  • 不,只有匹配的字段“b”和“u”——尽管出于兴趣——很高兴知道。我怀疑 R.equals 可能会满足所有人的平等需求。
  • 我花了很多时间尝试使用 R.head 和 R.tail 解决这个问题 - 得出的结论是,这是一个棘手的问题......以某种方式迭代唯一列表,并从数据中为每个唯一匹配删除一个匹配似乎是正确的方法......但我还没有设法让组合正确。

标签: javascript ramda.js


【解决方案1】:

这似乎过于复杂且不太可能高效,但一种选择是:

const foo = pipe(
  project(['b', 'u']),
  reduce(
    ({results, foundOnce}, item) => contains(item, results)
      ? {results, foundOnce}
      : contains(item, foundOnce)
        ? {results: append(item, results), foundOnce}
        : {results, foundOnce: append(item, foundOnce)},
    {results: [], foundOnce: []}
  ), 
  prop('results')
)

foo(xs); //=> [{b: 'A', u: 'F'}, {b: 'M', u: 'T'}]

也许这个版本更容易理解,但需要对数据进行额外的迭代:

const foo = pipe(
  project(['b', 'u']),
  reduce(
    ({results, foundOnce}, item) => contains(item, foundOnce)
        ? {results: append(item, results), foundOnce}
        : {results, foundOnce: append(item, foundOnce)},
    {results: [], foundOnce: []}
  ),
  prop('results'),
  uniq
)

repl here

【讨论】:

  • 抱歉,我现在不能检查你的结果——我一直在外面庆祝——但我不得不完全佩服想出这个的那种扭曲的头脑......我会检查的早上起来,尽我所能投赞成票。你摇滚! - 仅供参考,我对解决方案比性能更感兴趣 - 匹配列表非常短。
【解决方案2】:

如果您不关心多次循环数据,您可以这样:

  • 使用pick(您自己的想法)创建仅包含相关道具的部分副本
  • 使用groupByhash 函数对相似对象进行分组。 (Alternatively: sort 先用groupWith(equals)
  • 使用values获取分组数组
  • 使用filter 过滤掉只有 1 项的数组(那些不是被欺骗的...)
  • 映射结果并使用map(head)返回每个数组的第一个元素

在代码中:

const containsMoreThanOne = compose(lt(1), length);
const hash = JSON.stringify; // Naive.. watch out for key-order!

const getDups = pipe(
  map(pick(["b", "u"])),
  groupBy(hash),
  values,
  filter(containsMoreThanOne),
  map(head)
);

getDups(data);

Ramda REPL 中的工作演示。

一种更加混合的方法是将所有这些逻辑限制在一个 reducer 中,但对我来说它看起来有点混乱......

const clean = pick(["b", "u"]);
const hash = JSON.stringify;
const dupReducer = hash => (acc, o) => {
    const h = hash(o);
    // Mutate internal state
    acc.done[h] = (acc.done[h] || 0) + 1;
    if (acc.done[h] === 2) acc.result.push(o);

    return acc;
  };


const getDups = (clean, hash, data) =>
  reduce(dupReducer(hash), { result: [], done: { } }, map(clean, data)).result;

getDups(clean, hash, data);

REPL

【讨论】:

    【解决方案3】:
      const arr = [];
      const duplicates = [];
      const values1 =  [
      { b: 'A', u: 'F', a: 'q' },
      { b: 'M', u: 'T', a: 'q' },
      { b: 'A', u: 'F', a: 'q' },
      { b: 'M', u: 'T', a: 'q' },
      { b: 'M', u: 'T', a: 'q' },
      { b: 'X', u: 'Y', a: 'q' },
      { b: 'X', u: 'G', a: 'q' },
     ];
     values1.forEach(eachValue => {
     arr.push(values(pick(['b', 'u'], eachValue)));
     });
     arr.forEach(fish => {
     if ( indexOf(fish, arr) !== lastIndexOf(fish, arr) ) {
       duplicates.push(zipObj(['b', 'u'], fish));
     }
    });
    
    [blog]: https://ramdafunctionsexamples.com/ "click here for updates"
    
    <https://ramdafunctionsexamples.com/>?
    

    【讨论】:

      【解决方案4】:

      不是 Ramda JS 的专家,但我认为以下应该可行:

      var p = [
          { "b": "A", "u": "F" },
          { "b": "A", "u": "F" },
          { "b": "A", "u": "F" },
          { "b": "A", "u": "F" },
          { "b": "A", "u": "F" },
          { "b": "M", "u": "T" }
      ];
      var dupl = n => n > 1;
      R.compose(
          R.map(JSON.parse),
          R.keys,
          R.filter(dupl),
          R.countBy(String),
          R.map(JSON.stringify)
      )(p)
      

      如果有请告诉我。

      【讨论】:

      • 更新答案以调用正确的 R 方法 (uniqBy)
      • 我相信问题是要查找具有重复项的元素集。
      • @StevenGoodman 确实如此。让我修复答案。感谢您指出。
      • @82Tuskers 不错的尝试,但我只想比较“b”和“u”,正如我提到的,还有其他字段。
      • 如果您只想比较这些字段,并且只想返回这些字段,那么您可以通过使用project(['b', 'u']) 启动管道来解决此问题。不过,我不喜欢JSON.parse/JSON.stringify 解决方案。它们可能不适合您的数据。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-10
      • 2018-08-30
      • 1970-01-01
      • 2015-06-07
      • 2018-05-25
      • 2012-12-07
      相关资源
      最近更新 更多