Project

General

Profile

Actions

Feature #16252

open

Hash#partition should return hashes

Added by Dan0042 (Daniel DeLorme) about 2 years ago. Updated 1 day ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:95336]

Description

Hash#partition is implemented by Enumerable so it just returns two arrays of arrays

{1=>2,3=>4}.partition{|k,|k==1} #=> [[[1, 2]], [[3, 4]]]

But I think it would make more sense to behave similarly to Hash#select and Hash#reject

{1=>2,3=>4}.partition{|k,|k==1} #=> [{1=>2}, {3=>4}]

Updated by Dan0042 (Daniel DeLorme) 2 days ago

Two years later, and today when using hash.partition I was surprised that it didn't return two hashes.
This has to be just an oversight, right?

Updated by zverok (Victor Shepelev) 1 day ago

I am afraid we don't have some particular rules for Enumerables to return their own class:

Set[*1..8].select(&:odd?)
# => [1, 3, 5, 7] -- not a Set

Making Hash#select and #reject return hashes was an ad-hoc decision (and quite useful at that!); maybe it would be good to consistently follow this decision for some core collections?

FWIF, I once made a gem to do that for any collection of your liking, and while working on it, I identified lists of methods where it is probably desirable to return same class: methods returning singular collection and methods returning several collections.

Updated by Dan0042 (Daniel DeLorme) 1 day ago

I think it's ok that we don't have some particular rules for Enumerables to return their own class. Rather than consistently follow a decision, I think it's good to evaluate each case on its own merits. That said, I can certainly imagine Set#select/reject/partition returning Sets. But that would have to be a separate proposal, maybe related to #16989.

Hash#reject has returned a Hash since at least 1.8. Making Hash#select return a Hash has indeed been quite useful, and it would also be useful if #partition did the same. It's a bit surprising that hash.select(&b) != hash.partition(&b).first

Updated by Dan0042 (Daniel DeLorme) 1 day ago

FWIW, current usage in gems is consistent with Hash, but I did find one incompatibility.

actionpack-6.0.0/lib/action_dispatch/routing/mapper.rb
267:            constraints.partition do |key, requirement|

subarrays then converted with Hash[subarray]

capybara-3.29.0/lib/capybara/selector/definition/element.rb
21:    booleans, values = options.partition { |_k, v| [true, false].include? v }.map(&:to_h)

subarrays converted with .map(&:to_h)

fog-openstack-1.0.9/lib/fog/openstack/image/v1/models/images.rb
32:            custom_properties, params = headers.partition do |k, _|

subarrays converted with .map { |p| Hash[p] }

fugit-1.3.3/lib/fugit/duration.rb
71:    INFLA_KEYS, NON_INFLA_KEYS =
72:      KEYS.partition { |k, v| v[:I] }

subarrays iterated with .each do |k, a|
However there is incompatibility with keys = INFLA_KEYS.dup; keys.unshift([ :mon, { s: Fugit::Duration.parse(mon).to_sec } ])

sequel-5.24.0/lib/sequel/plugins/auto_validations.rb
167:          not_null_cols, explicit_not_null_cols = db_schema.select{|col, sch| sch[:allow_null] == false}.partition{|col, sch| sch[:default].nil?}.map{|cs| cs.map{|col, sch| col}}

subarrays converted to keys with .map{|cs| cs.map{|col, sch| col}} would have same result with hash but could be written .map(:keys) instead

sprockets-3.7.2/lib/sprockets/cache/file_store.rb
167:          delete_caches, keep_caches = caches.partition { |filename, stat|

subarrays compatible with hash: .map(&:first) and .inject(0) { |sum, (_, stat)|

Actions

Also available in: Atom PDF