From 26966568d1f7b2ee53caaf151574234a45db5529 Mon Sep 17 00:00:00 2001 From: Mindaugas Pelionis Date: Thu, 21 Mar 2019 20:04:15 +0200 Subject: [PATCH] Added: other methods of slicing Hash values, when keys may not exist. --- README.md | 47 +++++++++++++++++++ ...-compact-vs-slice-values-vs-map-compact.rb | 31 ++++++++++++ code/hash/values_at-vs-map.rb | 25 ++++++++++ 3 files changed, 103 insertions(+) create mode 100644 code/hash/values_at-compact-vs-slice-values-vs-map-compact.rb create mode 100644 code/hash/values_at-vs-map.rb diff --git a/README.md b/README.md index 35013ef..4b024ac 100644 --- a/README.md +++ b/README.md @@ -1061,6 +1061,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