我有一个哈希表,例如:
filers = {query: 'nice post', sort: 'time_desc', post_type: 'blog'...limit: 100}
这是用于过滤响应数据的哈希表。我需要为此哈希表创建一个唯一的键,以便缓存响应。我可以将其键和值转换为单个字符串。寻找一些简单而高效的有趣答案。
我有一个哈希表,例如:
filers = {query: 'nice post', sort: 'time_desc', post_type: 'blog'...limit: 100}
这是用于过滤响应数据的哈希表。我需要为此哈希表创建一个唯一的键,以便缓存响应。我可以将其键和值转换为单个字符串。寻找一些简单而高效的有趣答案。
考虑以下两个步骤:
Convert the Hash to a string (or other serialized format) after sorting the output write-order based on key.
It is important that the values are sorted by key during this transformation so that Hashes with the same key/value pairs (but with a different key order) will yield the same output. More complicated/nested structures need additional handling and should ensure consistent output for equivalent objects.
To get started with the process, consider:
sorted_kv_pairs = hash.to_a.sort_by {|k,v| k.to_s}
Use a hashing function such as SHA-1 or SHA-256/160 to generate a 40-byte Unique ID from the previously serialized object.
The huge output space (and cryptography qualities) of these functions make it unfeasible for there to be a purposeful collision and thus lead to "unique IDs".
这是我基于这个答案的解决方案。
# Return a cache key based on params hash
def params_cache_key(params)
# Normalize parameter hash, change keys to a string, normalize key order, sort array values
normalized = params.transform_keys(&:to_s)
.transform_values { |v| v.is_a?(Array) ? v.sort : v }
.sort_by { |k, _| k }.each_with_object('') do |(k, v), cache_key|
cache_key << "#{k}:#{v}"
end
Digest::SHA1.hexdigest(normalized)
end
以下是一些测试用例
describe '#params_cache_key' do
def cache_key(params)
controller.send :params_cache_key, params
end
it 'should produce a sha1' do
expect(cache_key(foo: 'bar', bar: 'baz')).to match /^\h{40}$/
end
it 'should produce same hash for different key order' do
expect(cache_key(foo: 'bar', bar: 'baz')).to eq cache_key(bar: 'baz', foo: 'bar')
end
it 'should produce same hash for stringified keys' do
expect(cache_key(foo: 'bar', bar: 'baz')).to eq cache_key(foo: 'bar', 'bar' => 'baz')
end
it 'should work with nested parameters in different order' do
expect(cache_key(category: %w(foo bar))).to eq cache_key(category: %w(bar foo))
end
end
cache_key
方法正在引用 controller
,请将其移除。 - Dirk de Kok