Skip to content

Add Hash#values.compact vs select benchmark for non-nil values#236

Open
etagwerker wants to merge 1 commit into
mainfrom
hash-values-compact-benchmark
Open

Add Hash#values.compact vs select benchmark for non-nil values#236
etagwerker wants to merge 1 commit into
mainfrom
hash-values-compact-benchmark

Conversation

@etagwerker

Copy link
Copy Markdown
Member

What

Adds a Hash benchmark comparing three ways to collect the non-nil values of a hash:

  • Hash#select { |_k, v| v }.values
  • Hash#values.select { |v| v }
  • Hash#values.compact

Hash#values.compact wins by a wide margin (~16x faster than values.select).

Why a new PR instead of #124

This builds on the idea from #124 by @sFrenkie, but that benchmark's three expressions aren't equivalent: its test data uses boolean values (v < 0.5true/false) and contains no nil values. So compact filtered nothing while the two select variants also dropped false. The "fastest" result came from compact doing less work, not from a fair comparison.

This version fixes that:

  • The data actually contains nil values (k < 0.5 ? k : nil), so all three expressions return the same result.
  • Adds an equivalence guard that raises if they ever diverge.
  • Method names describe the technique (matching repo convention, e.g. key_fast/key_slow) instead of slow/fast/fastest.

Output

$ ruby -v code/hash/select-values-vs-values-select-vs-values-compact.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin25]
Comparison:
 Hash#values.compact:   580468.7 i/s
  Hash#values.select:    35489.9 i/s - 16.36x  slower
  Hash#select.values:    29101.7 i/s - 19.95x  slower

README entry added in the Hash section.

🤖 Generated with Claude Code

Builds on the idea from #124 (sFrenkie), comparing three ways to collect
the non-nil values of a hash:

- Hash#select { |_k, v| v }.values
- Hash#values.select { |v| v }
- Hash#values.compact

Uses data that actually contains nil values so all three return the same
result, plus an equivalence guard, making it a fair comparison.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new Hash performance benchmark to fairly compare three equivalent ways of collecting non-nil hash values, and documents the results in the README’s Hash section.

Changes:

  • Added a new benchmark script comparing Hash#select.values, Hash#values.select, and Hash#values.compact for filtering nil values.
  • Added a new README entry describing the tradeoffs and including sample benchmark output.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
README.md Adds a Hash section entry documenting the benchmark and explaining why values.compact is fastest.
code/hash/select-values-vs-values-select-vs-values-compact.rb Introduces the benchmark implementation plus an equivalence guard to ensure fair comparison.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +8 to +10
def select_values
HASH.select { |_k, v| v }.values
end
Comment on lines +12 to +14
def values_select
HASH.values.select { |v| v }
end
Comment on lines +20 to +22
# Sanity check: all three must return the same values.
raise "not equivalent" unless select_values.sort == values_select.sort &&
values_select.sort == values_compact.sort
Comment thread README.md
Comment on lines +898 to +899
> To collect the non-nil values of a hash, `Hash#select { |_k, v| v }.values` allocates an intermediate hash before extracting its values; <br>
> `Hash#values.select { |v| v }` skips the intermediate hash but still runs a block per element; <br>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants