diff --git a/README.md b/README.md index f147750..f529116 100644 --- a/README.md +++ b/README.md @@ -986,6 +986,53 @@ Comparison: Hash#slice#values: 4817081.6 i/s - 1.57x slower ``` +##### `Hash#values_at` vs `Array#map { Hash#[] }` +[code](code/hash/values_at-vs-map.rb) + +To select hash values by keys, when some of the keys may not exist in the hash, +and you care about the default values. + +``` +$ ruby -v code/hash/values_at-vs-map.rb +ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-darwin18] +Warming up -------------------------------------- +Hash#values_at 245.809k i/100ms +Array#map { Hash#[] } 185.153k i/100ms +Calculating ------------------------------------- +Hash#values_at 5.284M (± 3.3%) i/s - 26.547M in 5.030606s +Array#map { Hash#[] } 3.104M (± 2.7%) i/s - 15.553M in 5.014067s + +Comparison: +Hash#values_at : 5283787.1 i/s +Array#map { Hash#[] }: 3104255.1 i/s - 1.70x slower +``` + +##### `Hash#slice#values` vs `Hash#values_at#compact` vs `Array#map { Hash#[] }#compact` +[code](code/hash/values_at-vs-map.rb) + +To select hash values by keys, when some of the keys may not exist in the hash, +and you care only about the intersection (i.e. ignore the default values). + +NOTE: `#compact`-based methods only work when the default value of `Hash` is +`nil`. + +``` +$ ruby -v code/hash/values_at-compact-vs-slice-values-vs-map-compact.rb +ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-darwin18] +Warming up -------------------------------------- +Hash#slice#values 227.519k i/100ms +Hash#values_at#compact 211.820k i/100ms +Array#map#compact 159.760k i/100ms +Calculating ------------------------------------- +Hash#slice#values 4.420M (± 1.5%) i/s - 22.297M in 5.046173s +Hash#values_at#compact 3.926M (± 1.6%) i/s - 19.699M in 5.019481s +Array#map#compact 2.508M (± 2.2%) i/s - 12.621M in 5.034508s + +Comparison: +Hash#slice#values : 4419599.9 i/s +Hash#values_at#compact: 3925677.1 i/s - 1.13x slower +Array#map#compact : 2508230.2 i/s - 1.76x slower +``` ### Proc & Block diff --git a/code/hash/values_at-compact-vs-slice-values-vs-map-compact.rb b/code/hash/values_at-compact-vs-slice-values-vs-map-compact.rb new file mode 100644 index 0000000..761387d --- /dev/null +++ b/code/hash/values_at-compact-vs-slice-values-vs-map-compact.rb @@ -0,0 +1,31 @@ +require 'benchmark/ips' + +HASH = { + a: 'foo', + b: 'bar', + c: 'baz', + d: 'qux' +}.freeze + +# Some of the keys may not exist in the hash; we don't care about the default values. +KEYS = %i[a c e f].freeze + +# NOTE: This is the only correct method, if the default value of Hash may be not nil. +def fast + HASH.slice(*KEYS).values +end + +def slow + HASH.values_at(*KEYS).compact +end + +def slowest + KEYS.map { |key| HASH[key] }.compact +end + +Benchmark.ips do |x| + x.report('Hash#slice#values ') { fast } + x.report('Hash#values_at#compact') { slow } + x.report('Array#map#compact ') { slowest } + x.compare! +end diff --git a/code/hash/values_at-vs-map.rb b/code/hash/values_at-vs-map.rb new file mode 100644 index 0000000..0e860ac --- /dev/null +++ b/code/hash/values_at-vs-map.rb @@ -0,0 +1,25 @@ +require 'benchmark/ips' + +HASH = { + a: 'foo', + b: 'bar', + c: 'baz', + d: 'qux' +}.freeze + +# Some of the keys may not exist in the hash; we want to keep the default values. +KEYS = %i[a c e f].freeze + +def fast + HASH.values_at(*KEYS) +end + +def slow + KEYS.map { |key| HASH[key] } +end + +Benchmark.ips do |x| + x.report('Hash#values_at ') { fast } + x.report('Array#map { Hash#[] }') { slow } + x.compare! +end