在Ruby哈希表中如何在开头插入元素?

13

我有一个使用情况,其中我有一个现有的哈希:

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]

3
根据定义,哈希表是无序的。因此,无法保证哈希表的顺序。 - zeantsoi
4
自 Ruby 1.9 版本以来,它们在 Ruby 中被排序。 - Denis de Bernardy
2
@zeantsoi:但这并不重要,因为在最近的Ruby版本中,哈希表并不符合传统定义中的字典;它只是伪装成了一个。 - Denis de Bernardy
2
@zeantsoi:除了已经说过的,没有什么需要解释的了...除了作为传统的键/值存储之外,Ruby哈希还将它们的键列表维护为有序集合。它看起来像字典,也像字典,但它不是通常意义上的字典。 - Denis de Bernardy
1
哈希表在传统意义上不是“有序”的,也就是说,键/值对不会自动排序。在 Ruby 中,哈希表仅仅是按照插入顺序维护其键/值对的顺序。{'b' => 1, 'a' => 2} # => {"b"=>1, "a"=>2} 如果按照任何其他意义进行“排序”,键/值对的位置都会改变。 - the Tin Man
显示剩余4条评论
4个回答

38
response = {aa: 'aaa', bb: 'bbb'}
new_response = {new: 'new_value'}.merge(response)
# => {:new=>"new_value", :aa=>"aaa", :bb=>"bbb"}

1
请注意,在 Ruby 1.9 之前,哈希表是无序的。 - Elmatou
响应 = { aa: 'aaa', bb: 'bbb', cc: 'cc' } 响应.merge(cc: 'ccc')将会得到{ aa: 'aaa', bb: 'bbb', cc: 'ccc' }如果你执行{ cc: 'ccc' }.merge 响应那么返回值将是{ cc: 'cc', aa: 'aaa', bb: 'bbb' }这可能是 response.unshift(cc: 'ccc') 的预期返回值吗?如果预期的返回值应该是 { cc: 'ccc', aa: 'aaa', bb: 'bbb' },那么答案是什么? - kangkyu
1
如果使用ActiveSupport,可以这样编写:new_response = response.reverse_merge(new: 'new_value') - user2384183
2
在复杂度方面,它是O(n)。但通常,我们期望将一个键插入哈希表的时间复杂度为O(1)。 - new2cpp

3

尝试将其转换为数组,然后再转回来:

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

当修改更大哈希表的子集时,这将非常有用。 - Robin Winton

0

我认为你可以做到这一点:

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

或者像@sawa一样


0

这对我很有用,因为我希望“时间戳”在 Splunk 读取它的 JSON 的前 128 个字符内,所以我想重新排序哈希键以确保在转换 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')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接