Arrays Ruby中哈希数组的选择性合并

Arrays Ruby中哈希数组的选择性合并,arrays,ruby,hash,Arrays,Ruby,Hash,我有两个包含哈希的数组: a = [ {:umc=>"11VE", :title=>"FOOBARS"}, {:umc=>"1973", :title=>"ZOOBARS"}, {:umc=>"1140", :title=>"TINYBAR"}, ] b = [ {:umc=>"11VE", :code=>"23"}, {:umc=>"10EE", :code=>"99"}, {:umc=>"1140",

我有两个包含哈希的数组:

a = [
  {:umc=>"11VE", :title=>"FOOBARS"},
  {:umc=>"1973", :title=>"ZOOBARS"},
  {:umc=>"1140", :title=>"TINYBAR"},
]

b = [
  {:umc=>"11VE", :code=>"23"},
  {:umc=>"10EE", :code=>"99"},
  {:umc=>"1140", :code=>"44"},
  {:umc=>"1973", :code=>"55"},
]
并希望有选择地使用哈希将它们合并到另一个数组中,如下所示:

c = [
  {:umc=>"11VE", :title=>"FOOBARS", :code=>"23"},
  {:umc=>"1973", :title=>"ZOOBARS", :code=>"55"},
  {:umc=>"1140", :title=>"TINYBAR"} :code=>"44"},
]
bb = b.select { |f| a.any? { |h| h[:umc] == f[:umc] } }
  #=> [{:umc=>"11VE", :code=>"23"},
  #    {:umc=>"1140", :code=>"44"},
  #    {:umc=>"1973", :code=>"55"}] 
(a + bb).group_by { |g| g[:umc] }.map { |_,v| v.reduce(:merge) }
  #=> [{:umc=>"11VE", :title=>"FOOBARS", :code=>"23"},
  #    {:umc=>"1973", :title=>"ZOOBARS", :code=>"55"},
  #    {:umc=>"1140", :title=>"TINYBAR", :code=>"44"}]
我正在使用代码

combo=(a+b).group_by{|h| h[:umc]}.map{|k,v| v.reduce(:merge)}
这很好地合并了两个数组,但我希望结果只包括第一个数组中出现的项


作为第二个想法,最好能有两个结果,一个是由两个初始数组组合而成的项,另一个是包含第一个数组中的元素但不在第二个数组中的元素。

不是很优雅,但它在计算方面应该相当有效,因为它平均是O(n+m)。我怀疑你能比这更快得到它。缺点是它会占用大量空间

def selective_merge(a, b)
  merged = {}
  a.each {|v| merged[v[:umc]] = [v[:umc], v[:title]]}
  b.each {|v| merged[v[:umc]] = merged[v[:umc]] << v[:code] if merged[v[:umc]]}
  merged.map {|k,v| {:umc => v[0], :title => v[1], :code => v[2]}}
end
def选择性_合并(a、b)
合并={}
a、 每个{v |合并的[v[:umc]=[v[:umc],v[:title]}
b、 每个{v | merged[v[:umc]]=merged[v[:umc]]v[0],:title=>v[1],:code=>v[2]}
结束

您可以按如下方式修改代码:

c = [
  {:umc=>"11VE", :title=>"FOOBARS", :code=>"23"},
  {:umc=>"1973", :title=>"ZOOBARS", :code=>"55"},
  {:umc=>"1140", :title=>"TINYBAR"} :code=>"44"},
]
bb = b.select { |f| a.any? { |h| h[:umc] == f[:umc] } }
  #=> [{:umc=>"11VE", :code=>"23"},
  #    {:umc=>"1140", :code=>"44"},
  #    {:umc=>"1973", :code=>"55"}] 
(a + bb).group_by { |g| g[:umc] }.map { |_,v| v.reduce(:merge) }
  #=> [{:umc=>"11VE", :title=>"FOOBARS", :code=>"23"},
  #    {:umc=>"1973", :title=>"ZOOBARS", :code=>"55"},
  #    {:umc=>"1140", :title=>"TINYBAR", :code=>"44"}]
但这样计算
bb
会更有效:

require 'set'
umc_a_vals = a.map { |g| g[:umc] }.to_set
  #=> #<Set: {"11VE", "1973", "1140"}>
bb = b.select { |f| umc_a_vals.include(f[:umc]) }
  #=> [{:umc=>"11VE", :code=>"23"},
  #    {:umc=>"1140", :code=>"44"},
  #    {:umc=>"1973", :code=>"55"}] 
f
的计算与您的代码非常相似,因此我认为不需要解释

我们现在合并散列:

{ g[:umc]=g }
  #=> { "11VE"=>{:umc=>"11VE", :code=>"23"} }
如果
h
有一个键
“11VE”
,则进入
h
。为此,我们使用(也称为
merge!
)的形式,该形式使用块:

{ |_,o,n| o.merge(n) }
确定合并的两个哈希中存在的键的值

块变量等于:

_ #=> "11VE"
o #=> {:umc=>"11VE", :title=>"FOOBARS"}
n #=> {:umc=>"11VE", :code=>"23"}
因此,区块计算结果为:

o.merge(n)
  #=> {:umc=>"11VE", :title=>"FOOBAR, :code=>"23"}
这是
h[:umc]
的更新值

旁白:我使用局部变量
\uu
作为键的值,以提请注意在块计算中没有使用它这一事实。变量
o
n
通常分别用于表示“旧”值和“新”值

b
的剩余值构造的散列的合并到
h
中也是类似的

最后一步是提取
h
的值

作为第二个示例,假设:

a = [
  {:umc=>"11VE", :title=>"FOOBARS"},
  {:umc=>"11VE", :title=>"ZOOBARS", :author=>"Billy-Bob"},
  {:umc=>"1140", :title=>"TINYBAR"}
]
我们获得:

f = a.each_with_object({}) { |g,h| h.update(g[:umc]=>g) { |_,o,n| o.merge(n) } }  
  #=> {"11VE"=>{:umc=>"11VE", :title=>"ZOOBARS", :author=>"Billy-Bob"},
  #    "1140"=>{:umc=>"1140", :title=>"TINYBAR"}} 
b.each_with_object(f) do |g,h| 
  (h.update(g[:umc]=>g) { |_,o,n| o.merge(n) }) if h.key?(g[:umc])
end.values
  #=> [{:umc=>"11VE", :title=>"ZOOBARS", :author=>"Billy-Bob", :code=>"23"},
  #    {:umc=>"1140", :title=>"TINYBAR", :code=>"44"}] 
Cary提出了一种“更具可读性”的方式来表示合并,另外,无论我们是否希望对初始
a
数组进行变异,我们都应该
dup
它的元素:

b.reduce(a.map(&:dup)) do |memo, e| 
  ae = memo.detect { |ae| ae[:umc] == e[:umc] }
  ae.merge!(e) if ae
  memo
end
试试这个

# @a - Pivot array(elements that will be in result array)
# @b - Array which contains intersecting elements
# @group - attribute name, which identify similarity of elements
def c_join(a, b, group)
  result = []
  a.each do |a_i|
    similar = b.detect{ |b_i| b_i[group] == a_i[group] }
    result << a_i.merge(similar)
  end
  result
end
结果

[
  {:umc=>"11VE", :title=>"FOOBARS", :code=>"23"}, 
  {:umc=>"1973", :title=>"ZOOBARS", :code=>"55"}, 
  {:umc=>"1140", :title=>"TINYBAR", :code=>"44"}
]

怎么样
:umc=>“10EE”
?很抱歉,10EE不应该出现在第一个数组中。毋庸置疑,实际数据超过10000项。感谢您的关注。您还可以通过传入select操作使其更通用,并使其在哈希中使用更多键。这是一个很好的答案,但我对Ruby和其他方面都还是新手。两个初始数组可能有任意数量的元素,因此如果element:umc匹配,我想调整代码以合并两个哈希的所有元素,而不必指定它们。这是一个很好的解决方案,但应该注意它会变异
a
的元素。依我看,如果把第一行分成两行,后者是
ae.merge,那就更好了!(e) 如果ae
。您还可以将
b.reduce(a)
更改为
(a+b)。如果
a
中的两个哈希值对于
:umc
相同,则reduce({})
。(这是我犯的一个错误,被OP标记出来。)@CarySwoveland我添加了
.dup
,以避免初始
数组的变异,谢谢。关于你的第二个评论,我没有得到一个要点。结果不会以任何方式切割
a
,它只会将其与
b
@CarySwoveland合并-第一个提议的代码实际上做了我想要的一切,或者至少我认为它做了。我的计划是查看所有其他解决方案,直到我理解为什么其中一些解决方案只包含以下结果:umc在两个阵列中都匹配。此解决方案按其应做的方式进行合并,并在根据匹配添加b中的元素时保持a。谢谢你们的解决方案和教育。@berlin欢迎你们。请注意减少
a
(正如Cary之前指出的那样,由于inplace
merge!
,我们改变了初始a数组)和
a.dup
之间的区别,后者不会改变任何初始输入。后者似乎更可靠。您的第二个示例运行得很好,但它似乎丢弃了a中与b中没有对应匹配的任何元素。我需要保留a的所有元素,不管与b的匹配如何。我只是不想要a中没有的b。我正在慢慢地研究每一个建议的解决方案,主要是为了让自己加快使用ruby(2.0版)的速度。(读者:我提供的第二种方法的问题是,如果
g
h
a
g[:umc]=h[:umc]
的元素,那么第一个散列将被第二个散列覆盖)。我已经修复了它,并给出了另一个例子。
[
  {:umc=>"11VE", :title=>"FOOBARS", :code=>"23"}, 
  {:umc=>"1973", :title=>"ZOOBARS", :code=>"55"}, 
  {:umc=>"1140", :title=>"TINYBAR", :code=>"44"}
]