为什么这两种方法产生不同的结果?

时间:2019-07-30 10:44:23

标签: arrays ruby append

根据所有文档,您可以使用<<.push+=将元素附加到数组,结果应该相同。我发现不是。有人可以向我解释我错了吗? (我使用的是Ruby 2.3.1。)

我有很多哈希。它们都包含相同的密钥。我想将它们结合在一起,以一个数组中的所有收集值形成一个哈希。这很简单,您遍历所有哈希并创建一个新哈希,收集所有值,如下所示:

    # arg is array of Hashes - keys must be identical
    return {} unless arg
    keys = (arg[0] ? arg[0].keys : [])

    result = keys.product([[]]).to_h # value for each key is empty array.

    arg.each do |h|
      h.each { |k,v| result[k] += [v] }
    end

    result
  end

如果我使用+=.push而不是使用<<而不是,则会得到完全奇怪的结果。

使用以下测试数组:

a_of_h = [{"1"=>10, "2"=>10, "3"=>10, "4"=>10, "5"=>10, "6"=>10, "7"=>10, "8"=>10, "9"=>10, "10"=>10}, {"1"=>100, "2"=>100, "3"=>100, "4"=>100, "5"=>100, "6"=>100, "7"=>100, "8"=>100, "9"=>100, "10"=>100}, {"1"=>1000, "2"=>1000, "3"=>1000, "4"=>1000, "5"=>1000, "6"=>1000, "7"=>1000, "8"=>1000, "9"=>1000, "10"=>1000}, {"1"=>10000, "2"=>10000, "3"=>10000, "4"=>10000, "5"=>10000, "6"=>10000, "7"=>10000, "8"=>10000, "9"=>10000, "10"=>10000}] 

我明白了

merge_hashes(a_of_h)
 => {"1"=>[10, 100, 1000, 10000], "2"=>[10, 100, 1000, 10000], "3"=>[10, 100, 1000, 10000], "4"=>[10, 100, 1000, 10000], "5"=>[10, 100, 1000, 10000], "6"=>[10, 100, 1000, 10000], "7"=>[10, 100, 1000, 10000], "8"=>[10, 100, 1000, 10000], "9"=>[10, 100, 1000, 10000], "10"=>[10, 100, 1000, 10000]} 

如我所料,但是如果我使用h.each { |k,v| result[k] << v },我会得到

buggy_merge_hashes(a_of_h)
 => {"1"=>[10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000], "2"=>[10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000], "3"=>[10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000], "4"=>[10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000], "5"=>[10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000], ...}

(剩下的我都剪掉了。)

我在这里不知道什么?

2 个答案:

答案 0 :(得分:1)

<<#push是破坏性操作(它们会更改接收方)。

+(因此也+=)是一种非破坏性操作(它返回一个新对象,而接收方保持不变)。

虽然他们似乎在做同一件事,但这种细微的差别至关重要。

这是由于另一个错误而引起的:result中的所有子数组都从同一对象开始。如果您要添加其中一个,则要添加所有它们。

如果您使用+=,为什么这不是问题?因为result[k] += [v]result[k] = result[k] += [v]是相同的(我躺在这里,有细微的差别,但这在这里并不重要,只是接受它们现在是相同的,不要感到困惑: D);并且由于+是非破坏性的,因此result[k] + [v]是与result[k]不同的对象;当您通过此分配更新数组中的值时,就不再使用起始[]对象,并且引用共享错误也无法再对您造成伤害。

创建result数组的更好方法是以下方法之一:

result = Array.new(keys.size) { [] }
result = keys.map { [] }

这将为每个元素创建一个新的数组对象。

但是,我会写得完全不同:

a_of_h.each_with_object(Hash.new { |h, k| h[k] = [] }) { |h, r|
  h.each { |k, v| r[k] << v }
}

each_with_hash将传递的对象作为附加参数提供给块(此处为r,以获取结果),并在方法完成后返回。参数-将位于r中的对象-将是带有default_proc的哈希值:每当我们尝试获取尚未在其中的键时,它将在其中插入一个新数组(即,尝试预先填充我们的结果对象,请按需进行)。然后,我们只需遍历数组中的每个哈希,然后将值插入结果哈希即可,而不必担心键是否存在。

答案 1 :(得分:0)

第一个执行var timeZone : String = String() override func viewDidLoad() { super.viewDidLoad() timeZone = getCurrentTimeZone() print(timeZone) } func getCurrentTimeZone() -> String { let localTimeZoneAbbreviation: Int = TimeZone.current.secondsFromGMT() let items = (localTimeZoneAbbreviation / 3600) return "\(items)" } 。第二个是hash[key] += value