Ruby 2.7:如何合并哈希数组的哈希并消除基于一个的重复项key:value

Ruby 2.7: How to merge a hash of arrays of hashes and eliminate the duplicates based on one key:value

我正在尝试为求职面试完成基于项目的评估,他们只在 Rails 的 Ruby 提供,我对此知之甚少。我正在尝试采用一个包含两个或多个数组哈希的哈希,并将这些数组组合成一个哈希数组,同时根据“id”:value 对消除重复的哈希。

所以我想接受这个:

h = {
  'first' =>
      [
        { 'authorId' => 12, 'id' => 2, 'likes' => 469 },
        { 'authorId' => 5, 'id' => 8, 'likes' => 735 },
        { 'authorId' => 8, 'id' => 10, 'likes' => 853 }
      ],
  'second' =>
      [
        { 'authorId' => 9, 'id' => 1, 'likes' => 960 },
        { 'authorId' => 12, 'id' => 2, 'likes' => 469 },
        { 'authorId' => 8, 'id' => 4, 'likes' => 728 }
      ]
}

然后把它变成这样:

[
  { 'authorId' => 12, 'id' => 2, 'likes' => 469 },
  { 'authorId' => 5, 'id' => 8, 'likes' => 735 },
  { 'authorId' => 8, 'id' => 10, 'likes' => 853 },
  { 'authorId' => 9, 'id' => 1, 'likes' => 960 },
  { 'authorId' => 8, 'id' => 4, 'likes' => 728 }

]

Ruby 有很多方法可以实现。

我的第一直觉是按 id 对它们进行分组,然后只从数组中选择第一项。

h.values.flatten.group_by{|x| x["id"]}.map{|k,v| v[0]}

更简洁的方法是在展平散列数组后根据 id 选择不同的项目,这正是 Cary Swoveland 在评论中建议的

h.values.flatten.uniq { |h| h['id'] }

TL;DR

适合您发布的数据的问题的最简单解决方案是 h.values.flatten.uniq。您可以在这里停止阅读,除非您想了解 为什么 您不需要关心这个特定数据集的重复 ID,或者什么时候您可能需要关心以及为什么这通常不那么直接比看起来的要多。

接近尾声时,我还提到了 Rails 的一些功能,这些功能解决了您不需要此特定数据的边缘情况。但是,它们可能会对其他用例有所帮助。

跳过ID-Specific重复数据删除;专注于删除重复的 哈希 而不是

首先,您 没有重复的 id 键,这些键也不属于重复的 Hash 对象。尽管 Ruby 实现 preserve entry order of Hash objects,但哈希在概念上是无序的。实际上,这意味着具有相同键和值的两个 Hash 对象(即使它们处于不同的插入顺序)仍然被认为是相等的。所以,也许不直观:

{'authorId' => 12, 'id' => 2, 'likes' => 469} ==
  {'id' => 2, 'likes' => 469, 'authorId' => 12}
#=> true

鉴于您的示例输入,您实际上不必担心此练习的唯一 ID。您只需要从合并的数组中删除重复的 Hash 对象,而您只有其中之一。

duplicate_ids =
  h.values.flatten.group_by { _1['id'] }
    .reject { _2.one? }.keys
#=> [2]

unique_hashes_with_duplicate_ids =
  h.values.flatten.group_by { _1['id'] }
    .reject { _2.uniq.one? }.count
#=> 0

如您所见,'id' => 2 是在两个哈希值中找到的唯一 ID,尽管是在相同的哈希对象中。由于您只有一个重复的哈希,因此问题已减少为展平存储在 h 中的哈希值数组,以便您可以从组合中删除任何重复的哈希元素(不是重复的 ID)数组.

已发布问题的解决方案

可能有一些用例需要处理哈希键的唯一性,但这不是其中之一。除非您想按某个键对结果进行排序,否则您真正需要的是:

h.values.flatten.uniq

由于没有要求您对合并数组中的 Hash 对象进行排序,因此您可以避免调用另一个方法调用(无论如何,在本例中)是 no-op.

如果没有额外的上下文,“独特性”可能会很棘手

查看 id 键的唯一原因是如果您在多个 unique 哈希对象中有重复的 ID,如果是这种情况,您然后就不得不担心要保留哪个 Hash 是正确的。例如,给定:

[ {'id' => 1, 'authorId' => 9, 'likes' => 1_920},
  {'id' => 1, 'authorId' => 9, 'likes' => 960} ]

这些记录中哪一个是“重复”记录?如果没有其他数据(例如时间戳),只需链接 uniq { h['id' } 或合并 Hash 对象即可分别获得第一条或最后一条记录。考虑:

[
  {'id' => 1, 'authorId' => 9, 'likes' => 1_920},
  {'id' => 1, 'authorId' => 9, 'likes' => 960}
].uniq { _1['id'] }
#=> [{"id"=>1, "authorId"=>9, "likes"=>1920}]

[
  {'id' => 1, 'authorId' => 9, 'likes' => 1_920},
  {'id' => 1, 'authorId' => 9, 'likes' => 960}
].reduce({}, :merge)
#=> {"id"=>1, "authorId"=>9, "likes"=>960}

利用像 Rails-Specific 时间戳特征这样的上下文

虽然上述唯一性问题似乎超出了您当前所问问题的范围,但了解任何类型的数据转换的局限性都是有用的。此外,知道 Rails 上的 Ruby 支持 ActiveRecord::Timestamp and the creation and management of timestamp-related columns within database migrations 可能在更广泛的意义上高度相关。

你不需要知道这些就可以回答原来的问题。但是,了解给定解决方案何时适合特定用例以及何时不适合也很重要。