如何为具有特定内容的哈希生成唯一标识符?

ber*_*kes 2 ruby hash

对于缓存层,我需要为哈希创建一个唯一的 sha。该哈希值的内容应该是唯一的。具有相同配置的两个哈希应该具有相同的 sha。

in_2014 = { scopes: [1, 2, 3], year: 2014 }
not_in_2104 = { scopes: [1, 2, 3], year: 2015 }
also_in_2014 = { year: 2014, scopes: [1, 2, 3] }

in_2014 == also_in_2014 #=> true
not_in_2104 == in_2014  #=> false
Run Code Online (Sandbox Code Playgroud)

现在,为了存储它并快速查找它,需要将其转换为 shasum 之类的东西。简单地转换为字符串是行不通的,因此从中生成十六进制摘要也行不通:

require 'digest'
in_2014.to_s == also_in_2014.to_s #=> false
Digest::SHA2.hexdigest(in_2014.to_s) == Digest::SHA2.hexdigest(also_in_2014.to_s) #=> false
Run Code Online (Sandbox Code Playgroud)

我想要的是一个 shasum 或其他一些标识符,它允许我将哈希值相互比较。我想要类似最后一个测试的东西,如果哈希的内容匹配,它将返回 true。

我之前可以对哈希值进行排序to_s,但这对我来说似乎很笨拙。一方面,我担心我忽略了那里的某些东西(sort一方面,a 返回一个数组,不再是哈希值)。有什么简单的事情是我忽略的吗?或者这根本不可能?

FWIW,我们在如下场景中需要这个:

Analysis.find_by_config({scopes: [1,2], year: 2014}).datasets
Analysis.find_by_config({account_id: 1337}).datasets

class Analysis < ActiveRecord::Base
  def self.find_by_config(config)
    self.find_by(config_digest: shasum_of(config))
  end

  def self.shasum_of(config)
     #WAT?
  end

  def before_saving
    self.config_digest = Analysis.shasum_of(config)
  end
end
Run Code Online (Sandbox Code Playgroud)

请注意,此处 Analysis 没有“scopes”或“year”或“account_id”。这些是任意配置,我们只需要查找数据集。

SHS*_*SHS 5

我不推荐这种hash方法,因为它不可靠。您可以通过{one: 1}.hash在 IRB 中执行 Rails 控制台中的相同命令,然后在另一台计算机上的 IRB 和/或 Rails 控制台中执行来快速确认这一点。输出会有所不同。

坚持下去Digest::SHA2.hexdigest(string)会更明智。

当然,您必须对哈希进行排序并将其字符串化。这就是我要做的:

hash.sort.to_s
Run Code Online (Sandbox Code Playgroud)

如果您出于某种原因不需要数组,请将其重新转换为散列。

Hash[hash.sort].to_s #=> will return hash
Run Code Online (Sandbox Code Playgroud)

而且,无论出于何种原因,如果您不想将哈希转换为数组然后再转换回哈希,请对哈希到排序哈希执行以下操作:

def prepare_for_sum( hash )
  hash.keys.sort.each_with_object({}) do |key, return_hash|
    return_hash[key] = hash[key]
  end.to_s
end
Run Code Online (Sandbox Code Playgroud)

对上述方法进行一些修改,您也可以对值进行排序;它对于数组或哈希值很有帮助。