我有2个对象数组,我想合并它们,使得结果数组将包含第一个数组中的所有元素,替换第二个数组具有相同id对象的任何元素。
finalArr=[]
arr1.each do |e1|
set2Contains=false
arr2.each do |e2|
if(e2.id==e1.id)
set2Contains=true
end
end
if(set2Contains)
finalArr.push(e2)
else
finalArr.push(e1)
end
end
我是红宝石的新手,但由于它是一个衬里的王者,上面看起来有点冗长。我想知道我的代码是否可以以任何方式缩短/优化?
感谢您的任何建议
答案 0 :(得分:5)
你想让你的第二个数组成为ID的哈希,所以你不必每次都扫描它:
hash = Hash.new
arr2.collect{|x| hash[x.id] = x}
那么你可以继续做下去:
finalArr = arr1.map{|x| hash.has_key?(x.id) ? hash[x.id] : x }
请注意,如果您的数组可以包含nil
,可能需要注意警告,在这种情况下,我假设情况并非如此。
答案 1 :(得分:1)
相同的逻辑,但精炼的代码:
final_arr=[]
arr1.each do |e1|
if arr2.any? { |e2| e1.id == e2.id }
final_arr << e2
else
final_arr << e1
end
end
更简洁
final_arr=[]
arr1.each do |e1|
final_arr << arr2.any? { |e2| e1.id == e2.id } ? e2 : e1
end
答案 2 :(得分:1)
既然你提到过单行,那么这是一个功能性的:
merged = Hash[ a1.map{|o| [o.id,o]} ].merge(Hash[ a2.map{|o| [o.id,o]} ]).values
这会将两个数组转换为id
键入的哈希值,合并它们(来自a2
的值会覆盖a1
中的值),然后只提取值。
如果您要对这些对象进行大量类似集合的工作,我建议您在其上定义eql?
和hash
方法来比较它们的id
值,然后只使用内置的Ruby Set
类:
require 'set'
Foo = Struct.new(:id,:name) do
def eql?(o2)
id==o2.id
end
def hash
id.hash
end
end
a1 = Set[ Foo.new(1,"Phrogz"), Foo.new(17,"Cat") ]
a2 = Set[ Foo.new(42,"Arthur"), Foo.new(1,"Gavin") ]
all = a1 + a2
all.each{ |foo| puts foo }
#=> #<struct Foo id=1, name="Phrogz">
#=> #<struct Foo id=17, name="Cat">
#=> #<struct Foo id=42, name="Arthur">
答案 3 :(得分:1)
在ruby 1.9中,它很简单:
(a|b).uniq{|x| x[:id]}
将数组放入您不想先替换的值。