我是否误解了Ruby中的String#hash方法?

7

我正在处理一批数据,但还没有在数据处理程序中编写重复项检查器,因此我预计会出现重复项。我运行了以下SQL查询:

SELECT     body, COUNT(body) AS dup_count 
FROM         comments
GROUP BY body
HAVING     (COUNT(body) > 1) 

获取重复项列表。研究后发现,这些重复项具有多个哈希值。最短的评论字符串是"[deleted]"。因此,让我们以此作为例子。在我的数据库中,有九个实例的评论为"[deleted]",并且在我的数据库中,这会生成1169143752200809218和1738115474508091027的两个哈希值。116出现了6次,173出现了3次。但是,在IRB中运行时,我得到了以下结果:

a = '[deleted]'.hash # => 811866697208321010

这是我用来生成哈希值的代码:
def comment_and_hash(chunk)     
  comment = chunk.at_xpath('*/span[@class="comment"]').text ##Get Comment##
  hash = comment.hash
  return comment,hash
end

我确认在我的代码中没有其他地方涉及到评论。这是我的datamapper类。

class Comment

    include DataMapper::Resource

    property :uid       , Serial
    property :author    , String
    property :date      , Date
    property :body      , Text
    property :arank     , Float 
    property :srank     , Float 
    property :parent    , Integer #Should Be UID of another comment or blank if parent
    property :value     , Integer #Hash to prevent duplicates from occurring

end

我理解的是,在同一个字符串上调用.hash方法,每次返回的结果都应该相同,这个理解正确吗?

如果我的字符串由"[deleted]"组成,那么正确的值是什么?

是否有一种方法可以在Ruby中使用不同的字符串,但是SQL会将它们视为相同的字符串?这似乎是发生这种情况的最合理解释,但我真的只是在瞎猜。

3个回答

9

如果你多次运行 ruby -e "puts '[deleted]'.hash",你会发现哈希值不同。事实上,只要 Ruby 进程存活,哈希值就会保持不变。原因在于 String#hash 会使用随机值进行种子生成。而 C 实现函数 rb_str_hash 使用了 rb_hash_start 来初始化这个随机种子,该种子在每次启动 Ruby 时都会重新初始化。

你可以使用像 Zlib#crc32 这样的 CRC,也可以使用 OpenSSL::Digest 的消息摘要之一,尽管后者可能过于复杂,因为对于重复项的检测,你可能不需要安全属性。


6
我使用以下方法来创建String#hash的替代方案,这些方案在时间和进程之间保持一致。
require 'zlib'

def generate_id(label)
  Zlib.crc32(label.to_s) % (2 ** 30 - 1)
end

我在运行时加上和不加上“%(2 ** 30 - 1)”部分,结果都一样。能否解释一下为什么要加上它以及它的作用是什么? - Noah Clark
1
我想将哈希值限制在小于2 ** 30的数字范围内。如果您将标签设置为非常长的字符串,则应从generate_id返回不同的值。 - Steve Wilhelm

2

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