在 Ruby Hash 的开头插入元素



我有一个用例,我有一个现有的哈希:

response = { aa: 'aaa', bb: 'bbb' }

我需要添加id作为键之一。

当我使用 response.merge(id: 'some_id') 然后将其转换为 JSON 时,我id 作为最后一个元素,这是我不想要的。

我想在response开头插入id: 'some_id'.我已经尝试过这个,但迭代它感觉不太好:

new_response = { id: 'some id' }
response.keys.reverse.each {|key| new_response[key] = response[key] }

基本上,我需要类似的功能,例如Ruby Array的unshift

irb(main):042:0> arr = [1, 2, 3]
=> [1, 2, 3]
irb(main):043:0> arr.unshift(5)
=> [5, 1, 2, 3]
response = {aa: 'aaa', bb: 'bbb'}
new_response = {new: 'new_value'}.merge(response)
# => {:new=>"new_value", :aa=>"aaa", :bb=>"bbb"}

尝试将其转换为数组并返回:

Hash[hash.to_a.unshift([k, v])]
我认为

你可以这样做:

response.inject({:new=>"new_value"}) { |h,(k,v)| h[k]=v; h }

或喜欢@sawa

这对我来说很有用,因为我希望"timestamp"在 json 的前 128 个字符内,以便 Splunk 读取它,所以我想重新排序哈希键以确保时间戳在转换to_json时始终位于开头。由于日志行会经常打印,我想确保最快的速度,检查不同的组合:

使用以下代码来确定哪种解决方案最快:

require 'securerandom'
require 'active_support/core_ext/hash/reverse_merge'
require 'fruity'
SMALL_HASH = { aa: 'aaa', bb: 'bbb' }.freeze
BIG_HASH = 10_000.times.each_with_object({}) do |_, obj|
  obj[SecureRandom.alphanumeric(6).to_sym] = SecureRandom.alphanumeric(128)
end; nil
puts "Running on #{RUBY_VERSION}"
# Running on 2.6.9
compare do
  _small_merge { { new: 'new_value' }.merge(SMALL_HASH) }
  _small_reverse_merge { SMALL_HASH.reverse_merge(new: 'new_value') }
  _small_unshift { Hash[SMALL_HASH.to_a.unshift([:new, 'new_value'])] }
end
# Running each test 32768 times. Test will take about 1 second.
# _small_merge is faster than _small_reverse_merge by 19.999999999999996% ± 10.0%
# _small_reverse_merge is faster than _small_unshift by 1.9x ± 0.1
compare(magnify: 1_000) do
  _big_merge { { new: 'new_value' }.merge(BIG_HASH) }
  _big_reverse_merge { BIG_HASH.reverse_merge(new: 'new_value') }
  _big_unshift { Hash[BIG_HASH.to_a.unshift([:new, 'new_value'])] }
end
# Running each test 1000 times.
# _big_reverse_merge is similar to _big_merge
# _big_merge is faster than _big_unshift by 2x ± 0.1

所以我建议使用以下策略

{ new: 'new_value' }.merge(aa: 'aaa', bb: 'bbb')

最新更新