Ruby on rails 如何对空数组的哈希使用默认值?

Ruby on rails 如何对空数组的哈希使用默认值?,ruby-on-rails,ruby,Ruby On Rails,Ruby,我想在需要时使用默认值重置我的ary。但是我不知道当ary的值改变时,如何不改变默认值 > default = {"a"=>[], "b"=>[], "c"=>[]} => {"a"=>[], "b"=>[], "c"=>[]} > ary = default.clone => {"a"=>[], "b"=>[], "c"=>[]} > ary["a"] << "foo" => ["fo

我想在需要时使用默认值重置我的ary。但是我不知道当ary的值改变时,如何不改变默认值

> default = {"a"=>[], "b"=>[], "c"=>[]}
=> {"a"=>[], "b"=>[], "c"=>[]} 

> ary = default.clone
=> {"a"=>[], "b"=>[], "c"=>[]} 

> ary["a"] << "foo"
=> ["foo"] 

> default
=> {"a"=>["foo"], "b"=>[], "c"=>[]} 
>default={“a”=>[],“b”=>[],“c”=>[]}
=>{“a”=>[],“b”=>[],“c”=>[]}
>ary=default.clone
=>{“a”=>[],“b”=>[],“c”=>[]}
>ary[“a”][“foo”]
>违约
=>{“a”=>[“foo”],“b”=>[],“c”=>[]}

您在这里发现的是,
散列#克隆
只进行浅层克隆,即它只复制自身,而不复制其中引用的对象

有许多“深度克隆”宝石可以解决这一特定问题,您也可以编写自己的宝石来解决这一问题:

class Hash
  def deep_clone
    Hash[collect { |k,v| [ k, v.respond_to?(:deep_clone) ? v.deep_clone : v ] }]
  end
end

class Array
  def deep_clone
    collect { |v| v.respond_to?(:deep_clone) ? v.deep_clone : v }
  end
end

这将允许您根据需要克隆任意哈希和数组对象。

克隆和dup都会创建对象的浅层副本,从而导致此行为。我不确定实现深度复制的正确方法是什么,但不是:

ary = default.clone
尝试:


这是从ruby 1.8.7上的live 2.3.8环境中获取的,clone只进行浅拷贝,这就是为什么克隆哈希仍然将所有内容都指向相同的嵌套数组

您可以通过
封送
类通过转储然后加载对象值来避免这种情况:

> default = {"a" => [], "b" => [], "c" => []}
=> {"a"=>[], "b"=>[], "c"=>[]} 
> ary = Marshal.load(Marshal.dump(default))
=> {"a"=>[], "b"=>[], "c"=>[]} 
> ary["a"] << "foo"
=> ["foo"]
> default
=> {"a"=>[], "b"=>[], "c"=>[]} 
>default={“a”=>[],“b”=>[],“c”=>[]}
=>{“a”=>[],“b”=>[],“c”=>[]}
>ary=封送处理加载(封送处理转储(默认))
=>{“a”=>[],“b”=>[],“c”=>[]}
>ary[“a”][“foo”]
>违约
=>{“a”=>[],“b”=>[],“c”=>[]}
类对象
def deep_克隆
封送:加载(封送转储(自))
结束
结束
默认值={“a”=>[],“b”=>[],“c”=>[]}
ary=默认值。深\u克隆
ary[“a”][],“b”=>[],“c”=>[]

执行此操作的方法如下:

ary = Marshal.load(Marshal.dump(default)) 

根据您想要执行的操作,编写深度克隆方法的一个更简单的替代方法可能是编写一个每次调用时都创建新默认数组的方法:

def default
  {"a"=>[], "b"=>[], "c"=>[]}
end

ary = default #=> {"a"=>[], "b"=>[], "c"=>[]}

ary["a"] << "foo" #=> {"a"=>["foo"], "b"=>[], "c"=>[]}

default #=> {"a"=>[], "b"=>[], "c"=>[]}
def默认值
{“a”=>[],“b”=>[],“c”=>[]}
结束
ary=默认值#=>{“a”=>[],“b”=>[],“c”=>[]}
ary[“a”]{“a”=>[“foo”],“b”=>[],“c”=>[]等
默认值#=>{“a”=>[],“b”=>[],“c”=>[]}

当然,如果默认散列的内容在程序运行过程中发生变化,那么这将不起作用,您必须研究克隆或编组技术,但如果内容已修复,这可能是一个更简单的解决方案。

谢谢您的帮助,但我收到了有关响应(:克隆)的语法错误语句。这只是在调用该对象之前检查该对象是否支持
deep\u clone
方法,因为您不能克隆数字或
true
false
等内容。实际上,深度克隆只需要容器类型的对象。Marshal无法处理您抛出的所有对象,所以要小心。此外,这肯定是执行这类任务最昂贵的方法之一。这就是为什么我说我不确定正确的方法是什么。我喜欢你的建议,因为这是一个更具体的解决方案,所以我投了赞成票;)奇怪的是,你会对一个方法使用
符号,而对另一个方法使用
符号,但总体上是+1。你能够用
::
调用方法总是让我感到奇怪和困惑。我能问一下你为什么这样做吗?它基本上是一个统计列表,分配给不同的状态,状态就是键。我定期想重置统计列表,但保持所有状态不变。
ary = Marshal.load(Marshal.dump(default)) 
def default
  {"a"=>[], "b"=>[], "c"=>[]}
end

ary = default #=> {"a"=>[], "b"=>[], "c"=>[]}

ary["a"] << "foo" #=> {"a"=>["foo"], "b"=>[], "c"=>[]}

default #=> {"a"=>[], "b"=>[], "c"=>[]}