我是Ruby编程的新手。在Ruby中,有很多方法可以编写优雅的代码。是否有任何优雅的方法可以通过属性值将两个数组与相同类型的对象链接起来?
很难解释。我们来看下一个例子:
a = [ { :id => 1, :value => 1 }, { :id => 2, :value => 2 }, { :id => 3, :value => 3 } ]
b = [ { :id => 1, :value => 2 }, { :id => 3, :value => 4 } ]
c = link a, b
# Result structure after linkage.
c = {
"1" => {
:a => { :id => 1, :value => 1 },
:b => { :id => 1, :value => 1 }
},
"3" => {
:a => { :id => 3, :value => 3 },
:b => { :id => 3, :value => 4 }
}
}
所以基本的想法是通过它们的公共ID从不同的数组中获取对象,并构造一个哈希,这将通过ID给出这对。
提前致谢。
如果你想通过Enumerable进行冒险,你可以这样说:
(a.map { |h| [:a, h] } + b.map { |h| [:b, h] })
.group_by { |_, h| h[:id] }
.select { |_, a| a.length == 2 }
.inject({}) { |h, (n, v)| h.update(n => Hash[v]) }
如果你真的想要键是字符串,请说n.to_s => Hash[v]
而不是n => Hash[v]
。
逻辑的工作原理如下:
:a
和:b
符号装饰小哈希来跟踪它们的起源。group_by
可以将事物分组成几乎最终格式。a
和b
中出现的条目。尺寸为1的组只出现在a
或b
中的一个,所以我们扔掉它们。inject
ion将事物重新排列成最终格式。请注意,我们在(1)中构建的数组恰好是Hash[]
正在寻找的格式。如果你想用link
方法做到这一点,那么你需要说:
link :a => a, :b => b
这样该方法将知道如何调用a
和b
。这个假设的link
方法也很容易推广到更多的数组:
def link(input)
input.map { |k, v| v.map { |h| [k, h] } }
.inject(:+)
.group_by { |_, h| h[:id] }
.select { |_, a| a.length == input.length }
.inject({}) { |h, (n, v)| h.update(n => Hash[v]) }
end
link :a => [...], :b => [...], :c => [...]
我认为,对于任何两个元素h1
和h2
(或a
),b
。
我会这样做:
h1[:id] != h2[:id]
注意:
def convert(arr) Hash[arr.map {|h| [h[:id], h]}] end
ah, bh = convert(a), convert(b)
c = ah.keys.each_with_object({}) {|k,h|h[k]={a: ah[k], b: bh[k]} if bh.key?(k)}
# => {1=>{:a=>{:id=>1, :value=>1}, :b=>{:id=>1, :value=>2}},
# 3=>{:a=>{:id=>3, :value=>3}, :b=>{:id=>3, :value=>4}}}
这是第二种方法。我也不喜欢它,但它代表了一种不同的方式来看问题。
ah = convert(a)
# => {1=>{:id=>1, :value=>1}, 2=>{:id=>2, :value=>2}, 3=>{:id=>3, :value=>3}}
bh = convert(b)
# => {1=>{:id=>1, :value=>2}, 3=>{:id=>3, :value=>4}}
这是正在发生的事情。第一步是只选择def sort_by_id(a) a.sort_by {|h| h[:id]} end
c = Hash[*sort_by_id(a.select {|ha| b.find {|hb| hb[:id] == ha[:id]}})
.zip(sort_by_id(b))
.map {|ha,hb| [ha[:id], {a: ha, b: hb}]}
.flatten]
的元素ha
,其中有a
的hb
元素b
。然后我们在ha[:id] = hb[id]
上对a
和b
进行排序(剩下的),h[:id]
将它们放在一起,然后制作哈希zip
。
c
好的,我自己找到了答案。这是一个非常短的代码行,应该可以解决这个问题:
r1 = a.select {|ha| b.find {|hb| hb[:id] == ha[:id]}}
# => [{:id=>1, :value=>1}, {:id=>3, :value=>3}]
r2 = sort_by_id(r1)
# => [{:id=>1, :value=>1}, {:id=>3, :value=>3}]
r3 = sort_by_id(b)
# => [{:id=>1, :value=>2}, {:id=>3, :value=>4}]
r4 = r2.zip(r3)
# => [[{:id=>1, :value=>1}, {:id=>1, :value=>2}],
# [{:id=>3, :value=>3}, {:id=>3, :value=>4}]]
r5 = r4.map {|ha,hb| [ha[:id], {a: ha, b: hb}]}
# => [[1, {:a=>{:id=>1, :value=>1}, :b=>{:id=>1, :value=>2}}],
# [3, {:a=>{:id=>3, :value=>3}, :b=>{:id=>3, :value=>4}}]]
r6 = r5.flatten
# => [1, {:a=>{:id=>1, :value=>1}, :b=>{:id=>1, :value=>2}},
# 3, {:a=>{:id=>3, :value=>3}, :b=>{:id=>3, :value=>4}}]
c = Hash[*r6]
# => {1=>{:a=>{:id=>1, :value=>1}, :b=>{:id=>1, :value=>2}},
# 3=>{:a=>{:id=>3, :value=>3}, :b=>{:id=>3, :value=>4}}}
Hash[a.product(b)
.select { |pair| pair[0][:id] == pair[1][:id] }
.map { |pair| [pair[0][:id], { :a => pair[0], :b => pair[1] }] }]
方法为我们提供了所有可能的对,然后我们通过对元素的相等ID来过滤它们。然后我们product
配对特殊形式,这将产生我们正在寻找的哈希。
所以map
回归Hash[["key1", "value1"], ["key2", "value2"]]
。我用它来得到我的问题的答案。
谢谢。
P.S。:您可以使用{ "key1" => "value1", "key2" => "value2" }
而不是pair.first
和pair[0]
而不是pair.last
以获得更好的可读性。
UPDATE
正如卡里指出的那样,最好用pair[1]
取代|pair|
来避免这些丑陋的指数:
|ha, hb|