简体   繁体   English

当减少输入的输入是哈希而不是哈希数组时,如何处理这种情况

[英]How to handle the case when input to reduce is hash rather than array of hashes

I have an incoming input which is sometimes an array of hashes and sometimes its just an individual hash. 我有一个输入,有时是一个哈希数组,有时只是一个单独的哈希。

hsh = {"property"=>[{"name"=>"first_name", "value"=>"Joe"}, {"name"=>"last_name", "value"=>"Doe"}]}

or 要么

hsh = {"property"=>{"name"=>"Foo", "value"=>"Bar"}}

From this input I am trying to generate a hash with name as key and value and as value , something like this: 从这个输入中,我试图生成一个namekeyvaluevalue的哈希,如下所示:

hsh['property'].reduce(HashWithIndifferentAccess.new) do |scan, kv_pair|
  scan.merge kv_pair['name'] => kv_pair['value']
end

Giving: 给予:

{"first_name"=>"Joe", "last_name"=>"Doe"}

This works well when hsh['property'] is an array of hashes but fails with the following error: hsh['property']是一个哈希数组,但由于以下错误而失败时,此方法效果很好:

TypeError: no implicit conversion of String into Integer 

when it's just an hash. 当它只是一个哈希。

How do I handle the reduce on hsh['property'] so it handles the case when input to it is hash rather than array of hashes? 如何处理hsh['property']上的reduce ,以便处理输入为哈希而不是哈希数组的情况?

It's easy to ensure something being an array. 确保某些东西是数组很容易。 You don't need if-else. 您不需要if-else。 Just wrap it in a new array, then flatten that array 1 level down. 只需将其包装在一个新阵列中,然后将该阵列向下平整1级即可。 I have to admit that this is not very performant, but I don't care if what I originally want to do is O(n) or higher (like mapping, reducing, sorting, whatever that requires traversing the array at least once). 我必须承认这不是很出色,但是我不在乎我最初想要的是O(n)还是更高(例如映射,归约,排序,无论需要至少遍历一次数组)。 I won't go this way if what I originally want to do is O(log(n)) or lower (like binary search). 如果我本来想做的是O(log(n))或更小(例如二进制搜索),我就不会这样。

[hsh['property']].flatten(1).reduce(HashWithIndifferentAccess.new) do |scan, kv_pair|
  scan.merge kv_pair['name'] => kv_pair['value']
end

The first thing to fix is to get rid of merge which creates an intermediate Hash each time through the loop. 要解决的第一件事是摆脱merge ,每次merge都会在循环中创建一个中间哈希。 That's a lot of garbage to collect. 收集了很多垃圾。 The second thing is to use the simpler each_with_index method that doesn't require chaining. 第二件事是使用不需要链接的简单的each_with_index方法。 This way you can just add data: 这样,您可以只添加数据:

hsh['property'].each_with_object(HashWithIndifferentAccess.new) do |kv_pair, scan|
  scan[kv_pair['name']] = kv_pair['value']
end

This will work so long as property has a series of Hash objects in an array. 只要property在数组中具有一系列Hash对象,此方法就起作用。 If you have just one you'll need to special case that: 如果只有一个,则需要特殊情况:

case hsh['property']
when Hash
  HashWithIndifferentAccess.new(
    hsh['property']['name'] => hsh['property']['value']
  )
when Array
  hsh['property'].each_with_object(HashWithIndifferentAccess.new) do |kv_pair, scan|
    scan[kv_pair['name']] = kv_pair['value']
  end
else
  # Uh-oh, you've got to handle this case of something random.
end

I'd convert the incoming parameter to an array of a single hash, then proceed: 我会将传入的参数转换为单个哈希的数组,然后继续:

def foo(aoh)
  aoh = [aoh] unless Array === aoh

  aoh # return it so we can see it's been changed

  # do stuff with the AoH
end

foo({a:1}) # => [{:a=>1}]
foo([{a:1},{b:2}]) # => [{:a=>1}, {:b=>2}]

Once it's an array of a single hash, or multiple hashes, you can iterate over the contents of the array with the same code. 一旦它是一个包含单个哈希或多个哈希的数组,就可以使用相同的代码遍历该数组的内容。

If you are responsible for generating the single hash or an array of hashes, you should always generate the same type of object, an array of hashes, which makes it easier for you to deal with it. 如果您负责生成单个哈希或哈希数组,则应始终生成相同类型的对象,即哈希数组,这将使您更轻松地处理它。 If you don't, then convert it to the same type of object and move on. 如果不这样做,则将其转换为相同类型的对象并继续。

As you are using rails, there is a dedicated method for ensuring something is an array - Array#wrap . 当您使用rails时,有一种专用方法来确保某些东西是一个数组Array#wrap Also note that your use of #reduce is considered unindiomatic and you should use #each_with_object instead. 另请注意,您对#reduce的使用被认为是不平凡的,应改用#each_with_object

Here is a possible solution: 这是一个可能的解决方案:

Array.wrap(hsh['property']).map { |kv| [kv['name'], kv['value']] }.to_h

If you are using ruby 2.3.0+, you can even take advantage of the cool new Hash#to_proc : 如果您使用的是ruby 2.3.0+,您甚至可以利用全新的Hash#to_proc

Array.wrap(hsh['property']).map { |kv| ['name', 'value'].map(&kv) }.to_h

If you insist, you can add the #with_indifferent_access at the end. 如果您坚持,可以在末尾添加#with_indifferent_access

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM