我有像这样的JSON数据结构......
{
"items": [
{
"person": { // person hash }
},
{
"dog": { // dog hash }
},
{
"fruit": { // fruit hash }
},
{
“person”: { // person hash }
}
]
}
}
数组中的每个项只包含一个键:值对。关键是机器人告诉我该值的项目类型。
我想做的是迭代数组并为每种类型的项目运行不同的函数。
所以我有这样的事情......
items = data.dig('items')
items.map do |item|
if person = item.dig('person')
transform_person(person)
elsif dog = item.dig('dog')
transform_dog(dog)
elsif fruit = item.dig('fruit')
transform_fruit(fruit)
end
end
但我觉得应该有更优雅的方式来做到这一点?
道歉。我似乎在我的问题中留下了一些含糊之处。
初始数组可能包含多个具有相同键的项目。我想要做的是映射到一系列项目,这些项目被转换为前端所需的内容。输入包含一个奇怪的结构和前端不需要的信息。
因此输出数组顺序必须与输入数组顺序匹配。
很抱歉这个混乱。
答案 0 :(得分:2)
首先,您需要在常量中定义键首选项:
PECKING_ORDER = %w[ person dog fruit ]
然后你可以用它来找到它:
def detect(item)
PECKING_ORDER.lazy.map do |key|
[ key, item.dig(key) ]
end.find do |key, v|
v
end
end
哪里可以挖出找到的第一个项目。 lazy
在这里被使用,因此它不会不必要地将它们全部挖掉,只需要一次一个地直到它们被击中。
这为您提供了一个键/值对,您可以将其用于动态分派:
items.each do |item|
key, value = detect(item)
if (key)
send(:"transform_#{key}", value)
end
end
答案 1 :(得分:1)
如果您知道映射,则可以创建伪工厂哈希:
methods_mapped = {
"person" => ->(person) { do_something_with_person(person) },
"dog" => ->(dog) { do_something_with_dog(dog) },
"fruit" => ->(fruit) { do_something_with_fruit(fruit) }
}
items.map do |item|
key = item.keys.first # what if keys.size > 1 ?
method = methods_mapped.fetch(key)
method.call(item[key])
end
或者你可以从相反的方向:
methods_mapped.each do |key, method|
method.call(items.dig(key))
end
答案 2 :(得分:1)
让f
成为一个给定的方法,将哈希作为参数。不失一般性,假设如下。这与OP的transform_person
,transform_dog
和transform_fruit
方法相结合。
def f(h)
case h.keys.first
when :person then "somebody"
when :dog then "doggie"
when :fruit then "juicy"
end
end
假设我们也被给予(这里不需要dig
)
items = data[:items]
#=> [{:person=>{:name=>"Melba"}},
# {:dog=>{:tricks=>[:roll_over, :shake_a_paw]}},
# {:fruit=>{:good=>"raspberries"}}]
和
key_order = [:bird, :marsupial, :dog, :person]
我们希望找到k
的第一个元素key_order
,其中items
包含h
的哈希h.key?(k) #=> true
。如果找到这样的哈希h
,我们将执行f(h)
。
首先计算哈希key_map
。
key_map = items.each_with_object({}) { |g,h| h[g.keys.first] = g }
#=> {:person=>{:person=>{:name=>"Melba"}},
# :dog=>{:dog=>{:tricks=>[:roll_over, :shake_a_paw]}},
# :fruit=>{:fruit=>{:good=>"raspberries"}}}
然后我们只需执行
k = key_order.find { |k| key_map[k] }
#=> :dog
k ? f(key_map[k]) : nil
#=> "doggie"
答案 3 :(得分:0)
我会保持简单:
items.map do |item|
do_something_with_person(item) if item.dig('person')
do_something_with_dog(item) if item.dig('dog')
do_something_with_fruit(item) if item.dig('fruit')
end
或
items.each do |item|
case item
when item.dig('person') then do_something_with_person(item)
when item.dig('dog') then do_something_with_dog(item)
when item.dig('fruit') then do_something_with_fruit(item)
end
end
或
def do_something(item)
case
when item.dig('person') then do_something_with_person(item)
when item.dig('dog') then do_something_with_dog(item)
when item.dig('fruit') then do_something_with_fruit(item)
end
end
items.map { |item| do_something(item) }