【问题标题】:Ruby iterating array of dictionariesRuby 迭代字典数组
【发布时间】:2018-09-04 22:06:42
【问题描述】:

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

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

抱歉。我的问题似乎有些模棱两可。

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

所以输出数组顺序必须与输入数组顺序一致。

很抱歉给您带来了困惑。

【问题讨论】:

  • 由于显而易见的原因,在发布答案后更改问题是不允许的。
  • @CarySwoveland wtf,哈哈!这是我听过的最离奇的事情。当然,即使在发布答案后,您也可以更改问题。这就是编辑按钮的用途。是的,这并不理想,但我意识到这个问题需要更多信息。哈哈!你是个有趣的人。
  • 这是真的,我很有趣,但在这种情况下我是认真的。对问题的更改可以而且经常会导致答案不正确甚至是荒谬的。此处给出的所有答案现在都可能存在错误,因为它们没有解决您的编辑问题,但大多数读者不会意识到编辑是在发布答案后完成的。编辑按钮用于澄清问题,而不是更改它。 (续)
  • (续)查看社区 Wiki 回答(关于编辑问题)here:“你编辑是为了让事情变得更好、更清晰、更有效——永远不会改变意义 [我的重点]。”这适用于任何编辑问题的人,包括提问者。另外:您的倒数第二段需要澄清。 (即使我没有机会使用这个词,我也会提到这一点。)
  • 我确实澄清了这个问题。我已经要求人们在自己回答问题后对自己的问题做出更大的改变。在我使用 StackOverflow 的 8 年里,我从未听说过任何关于编辑问题的规则。抱歉,您不喜欢编辑。但是有什么选择呢?把它留在这里,而不是得到我想要的问题的答案?删除它以复制它并在末尾添加一个句子?我知道你不应该问一个问题,然后把它改成完全不相关的东西(我以前见过),但这不是我所做的。

标签: arrays ruby hashmap


【解决方案1】:

首先,您需要在常量中定义键首选项:

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

【讨论】:

  • 嗨。我已经澄清了我的问题中的一些歧义。对不起,首先提出的模棱两可的问题。
  • 谢谢。明天会看看并尝试实施它。 :-)
【解决方案2】:

如果你知道映射,你可以做一个伪工厂哈希:

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

【讨论】:

  • 我喜欢这个想法(如果我不完全理解的话)。我明天会去看看。谢谢。
  • 我回答你的问题了吗?
【解决方案3】:

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]

我们希望找到key_order 的第一个元素k,其中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"

【讨论】:

  • 优秀。这需要一段时间才能沉入其中。哈哈。我明天会试试这个。谢谢
【解决方案4】:

我会保持简单:

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) }

【讨论】:

  • 第一个在item.dig(...)之后继续执行的是true。为什么item.dig 而不是简单的item.key?(...)
猜你喜欢
  • 2014-09-12
  • 1970-01-01
  • 1970-01-01
  • 2015-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多