【问题标题】:ruby select inner hash from nested hashruby 从嵌套哈希中选择内部哈希
【发布时间】:2018-04-24 05:08:11
【问题描述】:

我需要过滤嵌套散列以返回特定属性组合的项目。如果属性存在,则返回该哈希值,如果属性不存在,则返回默认值。如果该属性设置为“无”,则它不返回任何内容。考虑以下哈希:

{
  "size"=>{
    "default"=>{
      "jeans"=>"boyfriend"
     }, 
   "blue"=>"none"
 }, 
 "style"=>{
   "default"=>{
     "shoes"=>"boots"
    },
   "blue"=>{
     "jeans"=>"jeggings"
    }
  }
}

如果颜色是'黑色',那么

{
  "size"=>{
    "jeans"=>"boyfriend"
  }, 
  "style"=>{
    "shoes"=>"boots"
  }
}

或者如果颜色是“蓝色”,那么

{
  "size"=>{
  }, 
  "style"=>{
    "jeans"=>"jeggings"
  }
}

最好的方法是什么?我尝试了选择和删除的各种组合,但最终得到一个包含颜色键的数组或哈希。

【问题讨论】:

  • 你能提供你的代码示例吗?
  • 您遇到问题的代码是什么?你的代码有什么问题?您收到错误消息吗?错误信息是什么?你得到的结果不是你期望的结果吗?你期望什么结果,为什么,你得到的结果是什么,两者有什么不同?您正在观察的行为不是期望的行为吗?期望的行为是什么,为什么,观察到的行为是什么,它们有何不同?请提供minimal reproducible example
  • “最好的方法是什么?” – 你通过编写一个程序来做到这一点。如果您的程序有问题,请仔细阅读您正在使用的所有方法、类、模块和库的文档,为您的程序编写测试,用笔和纸跟踪执行,在调试器中单步执行,然后睡在上面,从头开始,再睡在上面,然后然后,然后Stack Overflow上提出一个有针对性的、狭隘的问题。
  • @JörgWMittag 公平评论,但可能有点矫枉过正

标签: ruby ruby-on-rails-4 hash


【解决方案1】:

假设h是问题中给出的哈希,如果我对问题的理解正确,以下方法将返回所需的哈希。

def doit(h, color)
  h.each_with_object({}) do |(k,f),g|
    c,v = f.find { |kk,_| kk != "default" }
    if c == color
      g[k] = v.is_a?(Hash) ? v : {}
    else
      g[k] = f["default"]
    end
  end
end

doit(h, 'black')
  #=> {"size"=>{"jeans"=>"boyfriend"}, "style"=>{"shoes"=>"boots"}}
doit(h, 'blue')
  #=> {"size"=>{}, "style"=>{"jeans"=>"jeggings"}}

第二个例子的步骤如下。

color = 'blue'

enum = h.each_with_object({})
  #=> #<Enumerator: {"size"=>{"default"=>{"jeans"=>"boyfriend"},
  #     "blue"=>"none"}, "style"=>{"default"=>{"shoes"=>"boots"},
  #     "blue"=>{"jeans"=>"jeggings"}}}:each_with_object({})>

生成此枚举器的第一个值:

x = enum.next
  #=> [["size", {"default"=>{"jeans"=>"boyfriend"}, "blue"=>"none"}], {}]

并传递给块。块变量设置为等于x,它们的值由“消歧”确定:

(k,f),g = x
k #=> "size"
f ##=> {"default"=>{"jeans"=>"boyfriend"}, "blue"=>"none"}
g #=> {}

现在执行块计算。

c,v = f.find { |kk,_| kk != "default" }
  #=> ["blue", "none"]
c #=> "blue"
v #=> "none"

作为

c == color
  #=> "blue" == "blue" => true

我们计算

v.is_a?(Hash)
  #=> false

因此执行分配

g[k] = {}
  #=> {}

现在就这样

g #=> {"size"=>{}}

h 的第二个和最后一个元素现在已生成并传递给块。

x = enum.next
  #=> [["style", {"default"=>{"shoes"=>"boots"},
  #     "blue"=>{"jeans"=>"jeggings"}}], {"style"=>{"jeans"=>"jeggings"}}]
(k,f),g = x
k #=> "style"
f #=> {"default"=>{"shoes"=>"boots"}, "blue"=>{"jeans"=>"jeggings"}}
g #=> {"size"=>"none"}
c,v = f.find { |kk,_| kk != "default" }
  #=> ["blue", {"jeans"=>"jeggings"}]
c #=> "blue"
v #=> {"jeans"=>"jeggings"}
c == color
  # "blue" == "blue" => true
v.is_a?(Hash)
  #=> true
g[k] = v
  #=> {"jeans"=>"jeggings"}
g #=> {"size"=>"none", "style"=>{"jeans"=>"jeggings"}}

并返回g

【讨论】:

  • 感谢详细的解释和使用 each_with_object 的好方法。我会试验一下。
【解决方案2】:

以下是我经过一些重构后得到的结果。它工作并且测试全部通过。可以进行更多重构。

class Filterer
  def self.filter(facets, color)
    acc = {}
    facets.each do |k, facets|
      facets.each do |_, facet|
        acc[k] = color_facets(color, facets)  
      end
    end

    acc
  end

  def self.color_facets(color, facets)
    return {} if no_facets?(color, facets)

    facets[color] ? facets[color] : facets['default']
  end

  def self.no_facets?(color, facets)
    facets[color] && facets[color] == 'no facet'
  end
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-21
    • 2019-08-08
    • 2021-02-03
    相关资源
    最近更新 更多