【问题标题】:Ruby: array of hashes - how to remove duplicates based on the hash key which is an arrayRuby:哈希数组 - 如何根据作为数组的哈希键删除重复项
【发布时间】:2016-09-15 18:03:52
【问题描述】:

我有一个哈希数组,其中每个哈希的键是和包含 2 个整数的数组 - 看起来像这样:

  [{[6, 8]=>0.5932190854209105}, {[6, 13]=>0.7183325285691291}, {[6, 15]=>0.8253727388780498}, {[8, 6]=>0.5932190854209105}, {[8, 13]=>0.7255537819950661}, {[8, 15]=>0.5249232568337963}, {[13, 6]=>0.7183325285691291}, {[13, 8]=>0.7255537819950661}, {[13, 15]=>0.6348636166265346}, {[15, 6]=>0.8253727388780497}, {[15, 8]=>0.5249232568337963}, {[15, 13]=>0.6348636166265343}]

我需要删除重复项 - 在这种情况下,重复项被定义为其键已经存在(但顺序相反)的哈希。例如 [6, 15] 和 [15,6]。您可以看到,根据这个定义,其中一半是重复的。

只是补充一下:

这是由以下组成的

 @user_array.each do |u|
   @result << @user_array.map { |p| Hash[[u, p] => kappa(u, p, "ipf")] if p !=u  }
 end

user_array 是一个整数数组(用户 ID)。例如:

  [6, 8, 13, 15]

我需要对每个无序配对组合运行 kappa 助手。我似乎可以弄清楚如何防止它“加倍”。我想如果我能以某种方式保存这对,那么我可以进行比较。我知道如何做到这一点的唯一方法是使用哈希。我相当新。

编辑:我试过这样排序:

@user_array.each do |u|
   @result << @user_array.map { |p| Hash[[u, p].sort => kappa(u, p, "ipf")] if p !=u  }
end

但它们是离散的散列......所以它不起作用:

 [{[6, 8]=>0.5932190854209105}, {[6, 13]=>0.7183325285691291}, {[6, 15]=>0.8253727388780498}, {[6, 8]=>0.5932190854209105}, {[8, 13]=>0.7255537819950661}, {[8, 15]=>0.5249232568337963}, {[6, 13]=>0.7183325285691291}, {[8, 13]=>0.7255537819950661}, {[13, 15]=>0.6348636166265346}, {[6, 15]=>0.8253727388780497}, {[8, 15]=>0.5249232568337963}, {[13, 15]=>0.6348636166265343}]

它不是那么简单。

【问题讨论】:

  • 我不确定我是否同意您对“重复”的定义。键是数组中包含的对;否则,哈希功能将能够“照顾”他们的条目,而无需您干预。你是如何插入钥匙的?
  • 我想我知道你在说什么......但它们是离散的散列,彼此了解
  • 你能举出更多例子吗?
  • 该解决方案非常混乱,并产生了惊人数量的垃圾。
  • 好的。据我所知,你能解释一下最后的评论吗?

标签: arrays ruby hash


【解决方案1】:

只要您的kappa 函数为u,p 生成与p,u 相同的值,那么您就可以这样做:

@result = @user_array.each_with_object({ }) do |u, h|
  @user_array.each do |p|
    next if (u == p)

    h[[u, p].sort] ||= kappa(u, p, "ipf")
  end
end

这只会一次且一次地填充值。如果您想在最后一个值不变的地方执行此操作,请将||= 更改为=

【讨论】:

  • 一个小补充
  • 我必须添加 - 除非 u==p 在第 3 行,否则这太棒了。我想知道为什么我的回答很垃圾 - 我敢肯定它是,但如果可能的话,我想了解一些知识
  • “废话”是对实际有效的代码的严格评估。 “次优”是我所说的。创建N个hash然后将它们合并在一起,每个操作都会创建另一个中间hash,非常浪费,每个都需要垃圾收集器处理。创建一个哈希并将所有内容插入其中会更简洁。
  • 添加了next 语句以在值相同时跳过插入。我没有在原版中看到您的if,因为您必须滚动才能看到它。这就是为什么我主张不要使用尾随 ifunless 子句,除非在处理 nextbreakreturn 之类的事情时。
【解决方案2】:

如果你对数组进行排序,你似乎可以在传递中阻止它。由于您说对的任何排列都是等效的,因此插入前的排序将允许散列消除/覆盖任何重复值。

@user_array.each do |u|
   @result << @user_array.map { |p| Hash[[u, p].sort => kappa(u, p, "ipf")] if p !=u  }
 end

【讨论】:

  • 如果你保持键排序,就不可能有重复。
  • 看看数组,它不是一个哈希,它是一个单独的哈希数组。
【解决方案3】:

在评论中,OP 已经澄清,如果要保留数组的元素(哈希),并且该哈希的(唯一)键是 [a,b],则不会使用键 [a,b] 或 @987654325 进行后续哈希@ 将被保留。

arr 表示您的哈希数组,每个哈希数组都有一个键/值对。

您可以使用Enumerable#uniqEnumerable#uniq,具体取决于是否要对arr 进行就地修改。

arr.uniq { |h| h.first.first.sort }
  #=> [{[6, 8]=>0.5932190854209105}, {[6, 13]=>0.7183325285691291},
  #    {[6, 15]=>0.8253727388780498}, {[8, 13]=>0.7255537819950661},
  #    {[8, 15]=>0.5249232568337963}, {[13, 15]=>0.6348636166265346}] 

或者,修改arr

arr.uniq! { |h| h.first.first.sort } || arr
  #=> [{[6, 8]=>0.5932190854209105}, {[6, 13]=>0.7183325285691291},
  #    {[6, 15]=>0.8253727388780498}, {[8, 13]=>0.7255537819950661},
  #    {[8, 15]=>0.5249232568337963}, {[13, 15]=>0.6348636166265346}] 
arr
  #=> [{[6, 8]=>0.5932190854209105}, {[6, 13]=>0.7183325285691291},
  #    {[6, 15]=>0.8253727388780498}, {[8, 13]=>0.7255537819950661},
  #    {[8, 15]=>0.5249232568337963}, {[13, 15]=>0.6348636166265346}] 

如果arr 不包含重复项,则需要|| arr,在这种情况下uniq! 返回nil

你也可以写

require 'set'
arr.uniq { |h| h.first.first.to_set }

(或uniq!)。

引用uniq的文档,“self按顺序遍历,并保留第一次出现。”

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-05
    • 2011-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多