使用Ramda.js从一个包含特定属性的对象数组中获取重复项集合

5
给定这个包含JavaScript对象(JSON)的数组:
每个对象都有一个b属性和一个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,而underscore并不仅仅提供一组重复项 - 就像问题所述,因此在有人能够使用ramda回答问题之前,我将保留问题的开放状态。在问题得到解答之前,我会继续使用underscore。

我有一个repl,它可以找到唯一的值...作为一个起点...


你想要完全的重复项还是只匹配 bu 的项? - Scott Sauyet
不,只有匹配的字段“b”和“u” - 虽然出于兴趣 - 知道这一点会很好。我怀疑R.equals可能适用于所有相等的情况。 - Jim
1
我花了很多时间尝试使用R.head和R.tail来解决这个问题 - 得出结论这是一个非常棘手的问题... 尝试以某种方式迭代遍历唯一列表,并删除每个唯一匹配项的数据中的一个匹配项似乎是正确的方法... 但我还没有成功地得到正确的组合。 - Jim
4个回答

3
这似乎过于复杂,而且不太可能高效执行,但其中一个选项是这样的:
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


1
抱歉,我现在无法检查您的结果 - 我一直在庆祝 - 但我必须完全钦佩那种想出这个东西的扭曲思维...我会在明天检查并尽力点赞。你太棒了!- FYI我更关心解决方案而不是性能 - 匹配列表非常短。 - Jim

1
如果您不介意多次循环遍历数据,可以尝试以下方法:
  • 使用pick(您自己的想法)创建只包含相关属性的部分副本
  • 使用带有哈希函数的groupBy将相似的对象分组。(或者:先sort,然后使用groupWith(equals))
  • 使用values获取分组数组
  • 使用filter过滤掉只有一个项目的数组(那些不是重复的...)
  • 映射结果并使用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


0
  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/>?

-1

虽然我不是 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)。 - trk
1
我认为这个问题是在寻找具有重复的元素集合。 - Steven Goodman
2
@StevenGoodman 的确。让我修正答案。感谢您的指出。 - trk
@82Tuskers,你的尝试很不错,但我只想比较“b”和“u”,正如我之前提到的,还有其他字段。 - Jim
如果您只想在这些字段上进行比较,并且也只想返回这些字段,那么您可以通过使用 project(['b', 'u']) 来开始管道来解决此问题。尽管如此,我并不喜欢 JSON.parse/JSON.stringify 的解决方案。因为它们可能不适用于您的数据。 - Scott Sauyet
我花了一个小时尝试使用R.pick,虽然接近成功,但我必须继续前进,我还为了更清晰而编辑了问题。 - Jim

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