Skip to content

Instantly share code, notes, and snippets.

@methodmissing
Created July 12, 2009 11:22
Embed
What would you like to do?
require 'benchmark'
class Hash
def diff(h2)
dup.delete_if { |k, v| h2[k] == v }.merge(h2.dup.delete_if { |k, v| has_key?(k) })
end
def faster_diff(h2)
dup.delete_if { |k, v| h2[k] == v }.merge!(h2.dup.delete_if { |k, v| has_key?(k) })
end
def deep_merge(other_hash)
merge(other_hash) do |key, oldval, newval|
oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
newval = newval.to_hash if newval.respond_to?(:to_hash)
oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
end
end
def faster_deep_merge(other_hash)
merge(other_hash) do |key, oldval, newval|
oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
newval = newval.to_hash if newval.respond_to?(:to_hash)
oldval.is_a?( Hash ) && newval.is_a?( Hash ) ? oldval.deep_merge(newval) : newval
end
end
def reverse_merge(other_hash)
other_hash.merge(self)
end
def reverse_merge!(other_hash)
replace(reverse_merge(other_hash))
end
def with_indifferent_access
hash = ActiveSupport::HashWithIndifferentAccess.new(self)
hash.default = self.default
hash
end
def with_faster_indifferent_access
hash = ActiveSupport::FasterHashWithIndifferentAccess.new(self)
hash.default = self.default
hash
end
def reverse_merge!(other_hash)
replace(reverse_merge(other_hash))
end
def faster_reverse_merge!(other_hash)
merge!( other_hash ){|k,o,n| o }
end
end
module ActiveSupport
class HashWithIndifferentAccess < Hash
def initialize(constructor = {})
if constructor.is_a?(Hash)
super()
update(constructor)
else
super(constructor)
end
end
def default(key = nil)
if key.is_a?(Symbol) && include?(key = key.to_s)
self[key]
else
super
end
end
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
alias_method :regular_update, :update unless method_defined?(:regular_update)
def []=(key, value)
regular_writer(convert_key(key), convert_value(value))
end
def update(other_hash)
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
self
end
alias_method :merge!, :update
def key?(key)
super(convert_key(key))
end
alias_method :include?, :key?
alias_method :has_key?, :key?
alias_method :member?, :key?
def fetch(key, *extras)
super(convert_key(key), *extras)
end
def values_at(*indices)
indices.collect {|key| self[convert_key(key)]}
end
def dup
HashWithIndifferentAccess.new(self)
end
def merge(hash)
self.dup.update(hash)
end
def reverse_merge(other_hash)
super other_hash.with_indifferent_access
end
def reverse_merge!(other_hash)
replace(reverse_merge( other_hash ))
end
def delete(key)
super(convert_key(key))
end
def stringify_keys!; self end
def symbolize_keys!; self end
def to_options!; self end
def to_hash
Hash.new(default).merge(self)
end
protected
def convert_key(key)
key.kind_of?(Symbol) ? key.to_s : key
end
def convert_value(value)
case value
when Hash
value.with_indifferent_access
when Array
value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
else
value
end
end
end
end
module ActiveSupport
class FasterHashWithIndifferentAccess < HashWithIndifferentAccess
def to_hash
Hash.new(default).merge!(self)
end
end
end
HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
FasterHashWithIndifferentAccess = ActiveSupport::FasterHashWithIndifferentAccess
first = { :one => 1, :two => 2, :three => 3 }
second = { :four => 4, :five => 5, :six => 6 }
first_indiff = first.with_indifferent_access
second_indiff = second.with_indifferent_access
first_faster_indiff = first.with_faster_indifferent_access
second_faster_indiff = second.with_faster_indifferent_access
tests = 10_000
Benchmark.bmbm do |results|
results.report("Hash#faster_diff") { tests.times { first.faster_diff( second ) } }
results.report("Hash#diff") { tests.times { first.diff( second ) } }
results.report("Hash#faster_deep_merge") { tests.times { first.faster_deep_merge( second ) } }
results.report("Hash#deep_merge") { tests.times { first.deep_merge( second ) } }
results.report("FasterHashWithIndifferentAccess#to_hash") { tests.times { first_faster_indiff.to_hash } }
results.report("HashWithIndifferentAccess#to_hash") { tests.times { first_indiff.to_hash } }
results.report("Hash#faster_reverse_merge!") { tests.times { first.faster_reverse_merge!( second ) } }
results.report("Hash#reverse_merge!") { tests.times { first.reverse_merge!( second ) } }
results.report("HashWithIndifferentAccess#reverse_merge!") { tests.times { first_indiff.reverse_merge!( second ) } }
end
=begin
macbook-pros-computer:~ lourens$ ruby as_bench.rb
Rehearsal ----------------------------------------------------------------------------
Hash#faster_diff 0.100000 0.000000 0.100000 ( 0.103207)
Hash#diff 0.120000 0.000000 0.120000 ( 0.118790)
Hash#faster_deep_merge 0.030000 0.000000 0.030000 ( 0.027385)
Hash#deep_merge 0.030000 0.000000 0.030000 ( 0.032819)
FasterHashWithIndifferentAccess#to_hash 0.030000 0.000000 0.030000 ( 0.027202)
HashWithIndifferentAccess#to_hash 0.030000 0.000000 0.030000 ( 0.032386)
Hash#faster_reverse_merge! 0.030000 0.000000 0.030000 ( 0.032605)
Hash#reverse_merge! 0.040000 0.000000 0.040000 ( 0.045057)
HashWithIndifferentAccess#reverse_merge! 0.360000 0.000000 0.360000 ( 0.362077)
------------------------------------------------------------------- total: 0.770000sec
user system total real
Hash#faster_diff 0.110000 0.000000 0.110000 ( 0.119254)
Hash#diff 0.130000 0.000000 0.130000 ( 0.137511)
Hash#faster_deep_merge 0.070000 0.000000 0.070000 ( 0.066942)
Hash#deep_merge 0.080000 0.000000 0.080000 ( 0.082390)
FasterHashWithIndifferentAccess#to_hash 0.020000 0.000000 0.020000 ( 0.021975)
HashWithIndifferentAccess#to_hash 0.030000 0.000000 0.030000 ( 0.034503)
Hash#faster_reverse_merge! 0.030000 0.000000 0.030000 ( 0.025488)
Hash#reverse_merge! 0.040000 0.000000 0.040000 ( 0.044840)
HashWithIndifferentAccess#reverse_merge! 0.350000 0.000000 0.350000 ( 0.352209)
=end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment