Ruby迭代字典数组

时间:2018-03-26 17:08:34

标签: arrays ruby hashmap

我有像这样的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

但我觉得应该有更优雅的方式来做到这一点?

道歉。我似乎在我的问题中留下了一些含糊之处。

初始数组可能包含多个具有相同键的项目。我想要做的是映射到一系列项目,这些项目被转换为前端所需的内容。输入包含一个奇怪的结构和前端不需要的信息。

因此输出数组顺序必须与输入数组顺序匹配。

很抱歉这个混乱。

4 个答案:

答案 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_persontransform_dogtransform_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) }