0

我想比较两个哈希并强制它们相等:

  • 一种在键值上带有符号
  • 第二个只有字符串。

例如:

sym_hash = {:id=>58, :locale=>:"en-US"}
string_hash = {"id"=>58, "locale"=>"en-US"}

尝试这样不起作用:

> sym_hash == string_hash
=> false

我首先尝试象征string_hash

> string_hash.deep_symbolize_keys
=> {:id=>58, :locale=>"en-US"}

但它仍然是错误的,因为前面sym_hash仍然有var。:locale

然后我尝试将以下内容字符串sym_hash

> sym_hash.with_indifferent_access
=> {"id"=>58, "locale"=>:"en-US"}

但是当我测试平等时,仍然是false出于同样的原因。

编辑

为了回答许多关于为什么我希望这些哈希在这里相等的评论,我将解释我正在尝试做什么。

Reque用来管理我的工作。现在我想做一门课,以避免运行相同的*作业,或者同时排入两次队列。

相同:对我来说,相同的工作是具有相同参数的工作,例如,我希望能够将具有不同 ID 的相同工作排入两次队列。)

为此,我正在使用插件resque-status,到目前为止,我能够知道作业何时运行。此外,当我使用保存参数时,set我注意到写入的消息Redis(因为 resque-status 正在使用 Redis 来跟踪作业的状态)没有用符号正确保存。

这是我的课:

# This class is used to run thread-lock jobs with Resque.
#
# It will check if the job with the exact same params is already running or in the queue.
# If the job is not finished, it will returns false,
# otherwise, it will run and returns a the uuid of the job.
#
class JobLock
  def self.run(obj, params = {})
    # Get rid of completed jobs.
    Resque::Plugins::Status::Hash.clear_completed
    # Check if your job is currently running or is in the queue.
    if !detect_processing_job(obj, params)
      job_uuid = obj.create(params)
      Resque::Plugins::Status::Hash.set(job_uuid,
                                        job_name: obj.to_s,
                                        params: params)
      job_uuid
    else
      false
    end
  end

  def self.detect_processing_job(obj, params = {})
    Resque::Plugins::Status::Hash.statuses.detect do |job|
      job['job_name'] == obj.to_s && compare_hashes(job['params'], params)
    end
  end

  def self.compare_hashes(string_hash, sym_hash)
    [sym_hash, string_hash].map do |h|
      h.map { |kv| kv.map(&:to_s) }.sort
    end.reduce :==
  end
end 

在这里我可以如何使用它:

JobLock.run(MyAwesomeJob, id: 58, locale: :"en-US")

如您所见,我使用了@mudasobwa 的答案,但我希望有一种更简单的方法来实现我想要做的事情!

4

5 回答 5

2

这个怎么样?

require 'set'

def sorta_equal?(sym_hash, str_hash)
  return false unless sym_hash.size == str_hash.size
  sym_hash.to_a.to_set == str_hash.map { |pair|
    pair.map { |o| o.is_a?(String) ? o.to_sym : o } }.to_set
end

sym_hash= {:id=>58, :locale=>:"en-US"}

sorta_equal?(sym_hash, {"id"=>58, "locale"=>"en-US"})           #=> true 
sorta_equal?(sym_hash, {"locale"=>"en-US", "id"=>58 })          #=> true 
sorta_equal?(sym_hash, {"id"=>58, "local"=>"en-US", "a"=>"b" }) #=> false 
sorta_equal?(sym_hash, {"id"=>58, "lacole"=>"en-US"})           #=> false 
sorta_equal?(sym_hash, {"id"=>58, [1,2,3]=>"en-US"})            #=> false 
sorta_equal?({}, {})                                            #=> true 

class A; end
a = A.new
sorta_equal?({:id=>a, :local=>:b}, {"id"=>a, "local"=>"b"})     #=> true 
于 2017-03-11T00:07:00.647 回答
1

以下版本用作 PHP 强制强制相等:

[sym_hash, string_hash].map do |h|
  h.map { |kv| kv.map(&:to_s) }.sort
end.reduce :==

顺便说一句,这不仅仅是因为我尊重使用智能手机的人。在宽度为 80 的终端上,它是一个完美的 oneliner。

仅将符号强制转换为字符串,保留数字以区别于它们的字符串表示:

[sym_hash, string_hash].map do |h|
  h.map { |kv| kv.map { |e| e.is_a?(Symbol) ? e.to_s : e } }.sort
end.reduce :==
于 2017-03-10T15:56:06.620 回答
1

localein的值sym_hash 是 Symbol :"en-US",而localein的值string_hash是 String。所以他们不是平等的。

现在,如果你这样做:

sym_hash = {:id=>58, :locale=>"en-US"}
string_hash = {"id"=>58, "locale"=>"en-US"}
string_hash.symbolize_keys!
sym_hash == string_hash
=> true
于 2017-03-10T16:16:25.717 回答
1

您可以尝试将两个哈希都转换为 JSON,然后比较它们:

require 'json'
# => true 
sym_hash = {:id=>58, :locale=>:"en-US"}
# => {:id=>58, :locale=>:"en-US"} 
string_hash = {"id"=>58, "locale"=>"en-US"}
# => {"id"=>58, "locale"=>"en-US"} 
sym_hash.to_json == string_hash.to_json
# => true 
于 2017-03-10T18:02:47.133 回答
0

最后,要回答我的问题,我不需要强制比较哈希值。我Marshal用来避免问题

class JobLock
  def self.run(obj, params = {})
    # Get rid of completed jobs.
    Resque::Plugins::Status::Hash.clear_completed
    # Check if your job is currently running or is in the queue.
    if !detect_processing_job(obj, params)
      job_uuid = obj.create(params)
      Resque::Plugins::Status::Hash.set(job_uuid,
                                        job_name: obj.to_s,
                                        params: Marshal.dump(params))
      job_uuid
    else
      false
    end
  end

  def self.detect_processing_job(obj, params = {})
    Resque::Plugins::Status::Hash.statuses.detect do |job|
      job['job_name'] == obj.to_s && Marshal.load(job['params']) == params
    end
  end
end 

无论如何,我把这个问题放在这里,因为也许它会在未来帮助一些人......

于 2017-03-15T18:57:31.737 回答