Skip to content

Commit c27969a

Browse files
committed
Add Hash#key? vs. Hash#keys.include? and Hash#value? vs. Hash#values.include?
1 parent a144a4c commit c27969a

File tree

2 files changed

+114
-3
lines changed

2 files changed

+114
-3
lines changed

README.md

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

808+
#### `Hash#key?` instead of `Hash#keys.include?` and `Hash#value?` instead of `Hash#values.include?` [code](code/hash/keys-include-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. <br>
812+
> `Hash#values.include?` allocates an array of values and performs an O(n) search; <br>
813+
> `Hash#value?` performs an O(n) search without allocating a new array.
814+
815+
```
816+
$ ruby -v code/hash/keys-include-vs-key.rb
817+
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
818+
819+
Calculating -------------------------------------
820+
Hash#key? (key is present)
821+
5.809M (± 2.1%) i/s - 29.038M in 5.000966s
822+
Hash#keys.include? (key is present)
823+
8.914k (± 3.9%) i/s - 44.564k in 5.007900s
824+
825+
Comparison:
826+
Hash#key? (key is present): 5809154.8 i/s
827+
Hash#keys.include? (key is present): 8913.6 i/s - 651.72x slower
828+
829+
Calculating -------------------------------------
830+
Hash#key? (key is absent)
831+
7.691M (± 1.9%) i/s - 38.520M in 5.010779s
832+
Hash#keys.include? (key is absent)
833+
2.991k (± 2.1%) i/s - 15.100k in 5.051475s
834+
835+
Comparison:
836+
Hash#key? (key is absent): 7690551.0 i/s
837+
Hash#keys.include? (key is absent): 2990.5 i/s - 2571.62x slower
838+
839+
Calculating -------------------------------------
840+
Hash#value? (value is present)
841+
14.780k (± 0.9%) i/s - 74.970k in 5.072806s
842+
Hash#values.include? (value is present)
843+
14.019k (± 4.4%) i/s - 70.533k in 5.041592s
844+
845+
Comparison:
846+
Hash#value? (value is present): 14780.1 i/s
847+
Hash#values.include? (value is present): 14019.0 i/s - 1.05x slower
848+
849+
Calculating -------------------------------------
850+
Hash#value? (value is absent)
851+
2.640k (± 2.1%) i/s - 13.200k in 5.002081s
852+
Hash#values.include? (value is absent)
853+
2.930k (± 6.1%) i/s - 14.994k in 5.144089s
854+
855+
Comparison:
856+
Hash#values.include? (value is absent): 2930.0 i/s
857+
Hash#value? (value is absent): 2640.1 i/s - 1.11x slower
858+
```
859+
808860
##### `Hash#merge!` vs `Hash#[]=` [code](code/hash/merge-bang-vs-\[\]=.rb)
809861

810862
```
@@ -1020,7 +1072,7 @@ Comparison:
10201072
##### `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)
10211073

10221074
The regular expression approaches become slower as the tested string becomes
1023-
longer. For short strings, `String#match?` performs similarly to
1075+
longer. For short strings, `String#match?` performs similarly to
10241076
`String#start_with?`/`String#end_with?`.
10251077

10261078
> :warning: <br>
@@ -1040,7 +1092,7 @@ $ ruby -v code/string/start-string-checking-match-vs-start_with.rb
10401092
String#=~ 1.088M (± 4.0%) i/s - 5.471M in 5.034404s
10411093
String#match? 5.138M (± 5.0%) i/s - 25.669M in 5.008810s
10421094
String#start_with? 6.314M (± 4.3%) i/s - 31.554M in 5.007207s
1043-
1095+
10441096
Comparison:
10451097
String#start_with?: 6314182.0 i/s
10461098
String#match?: 5138115.1 i/s - 1.23x slower
@@ -1055,7 +1107,7 @@ $ ruby -v code/string/end-string-checking-match-vs-end_with.rb
10551107
String#=~ 918.101k (± 6.0%) i/s - 4.650M in 5.084079s
10561108
String#match? 3.009M (± 6.8%) i/s - 14.991M in 5.005691s
10571109
String#end_with? 4.548M (± 9.3%) i/s - 22.684M in 5.034115s
1058-
1110+
10591111
Comparison:
10601112
String#end_with?: 4547871.0 i/s
10611113
String#match?: 3008554.5 i/s - 1.51x slower

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
require "benchmark/ips"
2+
3+
HASH = Hash[*("a".."zzz").to_a.shuffle]
4+
5+
def key_present_fast
6+
HASH.key? "z"
7+
end
8+
9+
def key_present_slow
10+
HASH.keys.include? "z"
11+
end
12+
13+
def key_absent_fast
14+
HASH.key? 3
15+
end
16+
17+
def key_absent_slow
18+
HASH.keys.include? 3
19+
end
20+
21+
def value_present_fast
22+
HASH.value? "z"
23+
end
24+
25+
def value_present_slow
26+
HASH.values.include? "z"
27+
end
28+
29+
def value_absent_fast
30+
HASH.value? 3
31+
end
32+
33+
def value_absent_slow
34+
HASH.values.include? 3
35+
end
36+
37+
Benchmark.ips do |x|
38+
x.report("Hash#key? (key is present)") { key_present_fast }
39+
x.report("Hash#keys.include? (key is present)") { key_present_slow }
40+
x.compare!
41+
end
42+
43+
Benchmark.ips do |x|
44+
x.report("Hash#key? (key is absent)") { key_absent_fast }
45+
x.report("Hash#keys.include? (key is absent)") { key_absent_slow }
46+
x.compare!
47+
end
48+
49+
Benchmark.ips do |x|
50+
x.report("Hash#value? (value is present)") { value_present_fast }
51+
x.report("Hash#values.include? (value is present)") { value_present_slow }
52+
x.compare!
53+
end
54+
55+
Benchmark.ips do |x|
56+
x.report("Hash#value? (value is absent)") { value_absent_fast }
57+
x.report("Hash#values.include? (value is absent)") { value_absent_slow }
58+
x.compare!
59+
end

0 commit comments

Comments
 (0)