Skip to content

Commit db5274c

Browse files
authored
Merge pull request #154 from JacobEvelyn/inefficient-hash-search
Add Hash#key? vs. Hash#keys.include? and Hash#value? vs. Hash#values.include?
2 parents a144a4c + 4b42f95 commit db5274c

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

README.md

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,41 @@ Comparison:
805805
Hash#keys.each: 869262.3 i/s - 1.21x slower
806806
```
807807

808+
#### `Hash#key?` instead of `Hash#keys.include?` [code](code/hash/keys-include-vs-\[\]-vs-key.rb)
809+
810+
> `Hash#keys.include?` allocates an array of keys and performs an O(n) search; <br>
811+
> `Hash#key?` performs an O(1) hash lookup without allocating a new array.
812+
813+
```
814+
$ ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
815+
816+
Calculating -------------------------------------
817+
Hash#keys.include? 8.612k (± 2.5%) i/s - 43.248k in 5.024749s
818+
Hash#key? 6.366M (± 5.5%) i/s - 31.715M in 5.002276s
819+
820+
Comparison:
821+
Hash#key?: 6365855.5 i/s
822+
Hash#keys.include?: 8612.4 i/s - 739.15x slower
823+
```
824+
825+
##### `Hash#value?` instead of `Hash#values.include?` [code](code/hash/values-include-vs-value.rb)
826+
827+
> `Hash#values.include?` allocates an array of values and performs an O(n) search; <br>
828+
> `Hash#value?` performs an O(n) search without allocating a new array.
829+
830+
```
831+
$ ruby -v code/hash/values-include-vs-value.rb
832+
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
833+
834+
Calculating -------------------------------------
835+
Hash#values.include? 23.187k (± 4.3%) i/s - 117.720k in 5.086976s
836+
Hash#value? 38.395k (± 1.0%) i/s - 194.361k in 5.062696s
837+
838+
Comparison:
839+
Hash#value?: 38395.0 i/s
840+
Hash#values.include?: 23186.8 i/s - 1.66x slower
841+
```
842+
808843
##### `Hash#merge!` vs `Hash#[]=` [code](code/hash/merge-bang-vs-\[\]=.rb)
809844

810845
```
@@ -1020,7 +1055,7 @@ Comparison:
10201055
##### `String#match` vs `String.match?` vs `String#start_with?`/`String#end_with?` [code (start)](code/string/start-string-checking-match-vs-start_with.rb) [code (end)](code/string/end-string-checking-match-vs-end_with.rb)
10211056

10221057
The regular expression approaches become slower as the tested string becomes
1023-
longer. For short strings, `String#match?` performs similarly to
1058+
longer. For short strings, `String#match?` performs similarly to
10241059
`String#start_with?`/`String#end_with?`.
10251060

10261061
> :warning: <br>
@@ -1040,7 +1075,7 @@ $ ruby -v code/string/start-string-checking-match-vs-start_with.rb
10401075
String#=~ 1.088M (± 4.0%) i/s - 5.471M in 5.034404s
10411076
String#match? 5.138M (± 5.0%) i/s - 25.669M in 5.008810s
10421077
String#start_with? 6.314M (± 4.3%) i/s - 31.554M in 5.007207s
1043-
1078+
10441079
Comparison:
10451080
String#start_with?: 6314182.0 i/s
10461081
String#match?: 5138115.1 i/s - 1.23x slower
@@ -1055,7 +1090,7 @@ $ ruby -v code/string/end-string-checking-match-vs-end_with.rb
10551090
String#=~ 918.101k (± 6.0%) i/s - 4.650M in 5.084079s
10561091
String#match? 3.009M (± 6.8%) i/s - 14.991M in 5.005691s
10571092
String#end_with? 4.548M (± 9.3%) i/s - 22.684M in 5.034115s
1058-
1093+
10591094
Comparison:
10601095
String#end_with?: 4547871.0 i/s
10611096
String#match?: 3008554.5 i/s - 1.51x slower

code/hash/keys-include-vs-key.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
require "benchmark/ips"
2+
3+
HASH = Hash[*("a".."zzz").to_a.shuffle]
4+
KEY = "zz"
5+
6+
def key_fast
7+
HASH.key? KEY
8+
end
9+
10+
def key_slow
11+
HASH.keys.include? KEY
12+
end
13+
14+
Benchmark.ips do |x|
15+
x.report("Hash#keys.include?") { key_slow }
16+
x.report("Hash#key?") { key_fast }
17+
x.compare!
18+
end

code/hash/values-include-vs-value.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
require "benchmark/ips"
2+
3+
HASH = Hash[*("a".."zzz").to_a.shuffle]
4+
VALUE = "zz"
5+
6+
def value_fast
7+
HASH.value? VALUE
8+
end
9+
10+
def value_slow
11+
HASH.values.include? VALUE
12+
end
13+
14+
Benchmark.ips do |x|
15+
x.report("Hash#values.include?") { value_slow }
16+
x.report("Hash#value?") { value_fast }
17+
x.compare!
18+
end

0 commit comments

Comments
 (0)