新手问题 关于 ruby array `dup` 很诡异 的问题

jhjguxin · 2013年11月28日 · 最后由 jhjguxin 回复于 2013年11月29日 · 3190 次阅读
a = [{a: 1, b: 3, c: 1}]
l = [1]

a.dup.select{|skill| l.include?(skill.delete(:a)) }
a
# => [{:b=>3, :c=>1}]


a = [{a: 1, b: 3, c: 1}]
l = [1]
b = a.dup
b.dup.select{|skill| l.include?(skill.delete(:a)) }
a
# => [{:b=>3, :c=>1}]

a = [{a: 1, b: 3, c: 1}]
l = [1]
a.dup.select{|skill| l.include?(skill.deep_dup.delete(:a)) }
# => [{:a=>1, :b=>3, :c=>1}]

# 只能这样 ??
a.select{|skill| l.include?(skill["a"]) }.delete_if{|k, v| k.to_s.eql?("a")}

a.dupduparray。里面的成员并没有 dup

2.0.0 (main):0 > a = [{}]
[
    [0] {}
]
2.0.0 (main):0 > a.object_id
70243821092280
2.0.0 (main):0 > a.first.object_id
70243821092660
2.0.0 (main):0 > b = a.dup
[
    [0] {}
]
2.0.0 (main):0 > b.object_id
70243877001940
2.0.0 (main):0 > b.first.object_id
70243821092660
2.0.0 (main):0 > a.first.object_id == b.first.object_id
true

@doitian 是的 我也这样 猜测的 所以

a = [{a: 1, b: 3, c: 1}]
# => [{:a=>1, :b=>3, :c=>1}]
a.dup[0] = {b: 1}
 a
#  => [{:a=>1, :b=>3, :c=>1}]
1.9.3-p448 :034 > a.select{|i| i = {c: 1}}
 => [{:a=>1, :b=>3, :c=>1}] 
1.9.3-p448 :035 > a
 => [{:a=>1, :b=>3, :c=>1}] 
1.9.3-p448 :036 > a.dup.select{|i| i.delete(:c)}
 => [{:a=>1, :b=>3}] 
1.9.3-p448 :037 > a
 => [{:a=>1, :b=>3}] 

不能对迭代的元素做 更新操作(赋值就没问题) 坑人

dup 不是浅拷贝么。。

@jarorwar 我 终于 把意识形态 形象化 了

#3 楼 @jhjguxin deep_dup 只是对嵌套的 hash 递归拷贝。你这个是数组里套 hash。

循环的时候数组元素是直接传 (准确说是转引用,就像调用了个方法 do_something(a[0]) 给 block 的,所以你用到 delete 这种 in place update 的方法,就会影响到数组中存的元素,因为它们指向同一个对象。

如果你想找出附合某个条件的东西,又想做修改,又不想影响原来的数组,应该是拷贝你要修改的数组元素,而不是数组本身。

a.select { |e| [1].include?(e[:a]) }.collect(&:dup).each { |e| e.delete(:a) }

# 或者用 Rails 的 Core Extension 里的 except
a.select { |e| [1].include?(e[:a]) }.collect { |e| e.except(:a) }

#6 楼 @doitian 是的 我的做法和你的也差不多 Rails 4.1activesupport 会支持 Array#deep_dup

array = [1, [2, 3]]
dup   = array.deep_dup
dup[1][2] = 4

array[1][2] # => nil
dup[1][2]   # => 4
需要 登录 后方可回复, 如果你还没有账号请 注册新账号