Ruby 在嵌套对象的未知级别上循环的DRY策略

Ruby 在嵌套对象的未知级别上循环的DRY策略,ruby,Ruby,我的场景是基于Gmail API的 我已经了解到,电子邮件可以根据各种因素对其消息部分进行深度或浅层嵌套,但主要是附件的存在 我使用的是gem,所以我没有使用JSON,我得到的是具有所有相同信息的对象,但我认为JSON表示使我更容易理解我的问题 一个简单的消息JSON响应如下所示(一个partsarray,其中包含两个哈希): 但是更复杂的带有附件的电子邮件的JSON响应看起来是这样的(现在partsnests本身有3层深): 是的,有一个方法在调用它自己。我知道,这就是我来这里的原因 这确实有

我的场景是基于Gmail API的

我已经了解到,电子邮件可以根据各种因素对其消息部分进行深度或浅层嵌套,但主要是附件的存在

我使用的是gem,所以我没有使用JSON,我得到的是具有所有相同信息的对象,但我认为JSON表示使我更容易理解我的问题

一个简单的消息JSON响应如下所示(一个
parts
array,其中包含两个哈希):

但是更复杂的带有附件的电子邮件的JSON响应看起来是这样的(现在
parts
nests本身有3层深):

是的,有一个方法在调用它自己。我知道,这就是我来这里的原因

这确实有效,我已经在几十条消息上测试过了,但是。。。必须有一种更好、更干燥的方法来做到这一点


当我不知道嵌套的深度时,如何递归地循环,然后向下移动一个级别,然后以干燥的方式再次循环?不需要经历所有这些痛苦。只需继续在
部分
字典中搜索,直到找到第一个不再有
部分
的值。此时,在
parts
变量中有了最后的
parts

代码:

reponse = {"id" => "175aee26de8209d2","snippet" => "snippet text...","payload" => {"parts" => [{"mimeType" => "multipart/related","parts" => [{"mimeType" => "multipart/alternative","parts" => [{"mimeType" => "text/plain","body" => {"data" => "hey, you found me! This is what I want!!"}},{"mimeType" => "text/html","body" => {"data" => "<div>I actually don't want this one.</div>"}}]},{"mimeType" => "image/jpeg"}]},{"mimeType" => "application/pdf"}]}}
parts   = reponse["payload"]
parts   = (parts["parts"].send("first") || parts["parts"]) while parts["parts"]
data    = parts["body"]["data"]
puts data
hey, you found me! This is what I want!!

没有必要经历这些痛苦。只需继续在
部分
字典中搜索,直到找到第一个不再有
部分
的值。此时,在
parts
变量中有了最后的
parts

代码:

reponse = {"id" => "175aee26de8209d2","snippet" => "snippet text...","payload" => {"parts" => [{"mimeType" => "multipart/related","parts" => [{"mimeType" => "multipart/alternative","parts" => [{"mimeType" => "text/plain","body" => {"data" => "hey, you found me! This is what I want!!"}},{"mimeType" => "text/html","body" => {"data" => "<div>I actually don't want this one.</div>"}}]},{"mimeType" => "image/jpeg"}]},{"mimeType" => "application/pdf"}]}}
parts   = reponse["payload"]
parts   = (parts["parts"].send("first") || parts["parts"]) while parts["parts"]
data    = parts["body"]["data"]
puts data
hey, you found me! This is what I want!!

您可以使用递归计算所需的结果

def find_it(h, top_key, k1, k2, k3)
  return nil unless h.key?(top_key)
  recurse(h[top_key], k1, k2, k3)
end

h1
h2
等于示例1中给出的两个哈希值。然后:


一,。哈希
h[:payload][:parts].last#=>{“mimeType”:“application/pdf”}
似乎包含导致问题的隐藏字符。因此,我从
h2
中删除了该散列。您可以使用递归计算所需的结果

def find_it(h, top_key, k1, k2, k3)
  return nil unless h.key?(top_key)
  recurse(h[top_key], k1, k2, k3)
end

h1
h2
等于示例1中给出的两个哈希值。然后:



一,。哈希
h[:payload][:parts].last#=>{“mimeType”:“application/pdf”}
似乎包含导致问题的隐藏字符。因此,我从
h2

中删除了该散列。您只需要第一个body值吗?递归解决方案没有什么特别糟糕的,我同意它。事实上,递归通常是解决这类任务的最干净的方法。当然,你总是有机会让事情失去控制,但是你真的认为在这种情况下——Gmail API——嵌套真的会那么深吗?您可以用堆栈烘焙的迭代遍历来替换递归,但这并不能解决内存问题——如果嵌套级别不受限制,您迟早会耗尽内存。跟踪深度并在深度太深时引发错误可能会有所帮助…@ZainArshad,我想要
body.data
其中
mime\u类型是“text/plain”@Chiperific通过查看模式,第一个包含body的部分肯定是将mime\u类型作为“text/plain”的部分@Chiperific您可以在各种响应上运行我的代码,并让我知道它是否工作正常,这将超出您只需要第一个body值的范围?递归解决方案没有什么特别糟糕的,我同意它。事实上,递归通常是解决这类任务的最干净的方法。当然,你总是有机会让事情失去控制,但是你真的认为在这种情况下——Gmail API——嵌套真的会那么深吗?您可以用堆栈烘焙的迭代遍历来替换递归,但这并不能解决内存问题——如果嵌套级别不受限制,您迟早会耗尽内存。跟踪深度并在深度太深时引发错误可能会有所帮助…@ZainArshad,我想要
body.data
其中
mime\u类型是“text/plain”@Chiperific通过查看模式,第一个包含body的部分肯定是将mime\u类型作为“text/plain”的部分@Chiperific您可以在各种响应上运行我的代码,并让我知道它是否工作正常,它只会在数据位于树的第一个分支时工作。我需要能够匹配
mime_type
,以确保我得到的是“文本/普通”数据。我想你已经知道如何解决这个问题了,现在是一个很好的时间来考虑如何添加最终检查我喜欢你要走的轨迹,但是我还需要实现另外两件事:1)检查
部分对象上的
mime\u类型
,2)在拒绝它们时弹出
部分
,这样我就可以继续调用对象部分上的
第一个
。我一直在玩这个,它的可读性并没有提高很多,也没有提高内存效率,因为我仍然在迭代所有东西。我要给你一个信用,因为我要求一个更干燥的方法,你给了它。这是有效的,你需要迭代每个散列,直到你找到正确的body标记,除了检查每个键之外没有其他方法。这只在数据在树的第一个分支中时有效。我需要能够匹配
mime_type
,以确保我得到的是“文本/普通”数据。我想你已经知道如何解决这个问题了,现在是一个很好的时间来考虑如何添加最终检查我喜欢你要走的轨迹,但是我还需要实现另外两件事:1)检查
部分对象上的
mime\u类型
,2)在拒绝它们时弹出
部分
,这样我就可以继续调用对象部分上的
第一个
。我一直在玩这个,它的可读性并没有提高很多,也没有提高内存效率,因为我仍然在迭代所有东西。我要给你一个积分,因为我要求了一个干巴巴的方法,你给了它。这是有效的,你需要迭代每个散列,直到你找到正确的body标记,除了检查没有别的方法
def find_it(h, top_key, k1, k2, k3)
  return nil unless h.key?(top_key)
  recurse(h[top_key], k1, k2, k3)
end
def recurse(h, k1, k2, k3)
  return nil unless h.key?(k1)      
  h[k1].each do |g|
    v = g.dig(k2,k3) || recurse(g, k1 , k2, k3)
    return v unless v.nil?
  end
  nil
end
find_it(h1, :payload, :parts, :body, :data)
  #=> "Hey, you found the body of the email! I want this!"
find_it(h2, :payload, :parts, :body, :data)
  #=> "hey, you found me! This is what I want!!"