Ruby:按属性值链接两个对象数组

问题描述 投票:1回答:3

我是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给出这对。

提前致谢。

ruby arrays hash mapping
3个回答
2
投票

如果你想通过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]

逻辑的工作原理如下:

  1. 我们需要知道所有东西来自哪里我们用:a:b符号装饰小哈希来跟踪它们的起源。
  2. 然后将装饰的数组一起添加到一个列表中,以便...
  3. group_by可以将事物分组成几乎最终格式。
  4. 然后找到大小为2的组,因为这些组包含ab中出现的条目。尺寸为1的组只出现在ab中的一个,所以我们扔掉它们。
  5. 然后一点点injection将事物重新排列成最终格式。请注意,我们在(1)中构建的数组恰好是Hash[]正在寻找的格式。

如果你想用link方法做到这一点,那么你需要说:

link :a => a, :b => b

这样该方法将知道如何调用ab。这个假设的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 => [...]

1
投票

我认为,对于任何两个元素h1h2(或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,其中有ahb元素b。然后我们在ha[:id] = hb[id]上对ab进行排序(剩下的),h[:id]将它们放在一起,然后制作哈希zip

c

1
投票

好的,我自己找到了答案。这是一个非常短的代码行,应该可以解决这个问题:

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.firstpair[0]而不是pair.last以获得更好的可读性。

UPDATE 正如卡里指出的那样,最好用pair[1]取代|pair|来避免这些丑陋的指数:

|ha, hb|
© www.soinside.com 2019 - 2024. All rights reserved.