Project

General

Profile

Feature #8499

Importing Hash#slice, Hash#slice!, Hash#except, and Hash#except! from ActiveSupport

Added by mrkn (Kenta Murata) over 4 years ago. Updated about 2 months ago.

Status:
Closed
Priority:
Normal
Target version:
[ruby-core:55330]

Description

According to my experiences, the following two idioms often appeare in application codes.

  1. hash = other_hash.select { |k, | [:key1, :key2, :key3].include? k }
  2. hash = other_hash.reject { |k, | [:key1, :key2, :key3].include? k }

On Rails, they can be written in the following forms by using ActiveSupport's features.

  1. hash = other_hash.slice(:key1, :key2, :key3)
  2. hash = other_hash.except(:key1, :key2, :key3)

I think the latter forms are shorter and more readable than the former ones.

So I propose to import the following methods from ActiveSupport:

  • Hash#slice
  • Hash#slice!
  • Hash#except
  • Hash#except!
patch.diff (4.05 KB) patch.diff built-in Hash#slice, Hash#slice!, Hash#except, and Hash#except! Glass_saga (Masaki Matsushita), 07/26/2013 10:58 PM
patch2.diff (4.07 KB) patch2.diff Glass_saga (Masaki Matsushita), 07/29/2013 12:24 PM
patch3.diff (4.04 KB) patch3.diff Glass_saga (Masaki Matsushita), 09/07/2017 10:09 AM
patch4.diff (4.5 KB) patch4.diff Glass_saga (Masaki Matsushita), 09/19/2017 05:41 AM

Related issues

Related to Ruby trunk - Feature #9108: Hash sub-selectionsClosed
Related to Ruby trunk - Feature #12461: Hash & keys to make subset.Rejected
Related to Ruby trunk - Feature #13563: Implement Hash#choice method.Closed

Associated revisions

Revision 60229
Added by glass about 2 months ago

hash.c: Add Hash#slice

  • hash.c (rb_hash_slice): add Hash#slice [Feature #8499]

History

#2 [ruby-core:55332] Updated by zzak (Zachary Scott) over 4 years ago

Hello,

On Thu, Jun 6, 2013 at 4:12 PM, mrkn (Kenta Murata) muraken@gmail.com wrote:

On Rails, they can be written in the following forms by using ActiveSupport's features.

  1. hash = other_hash.slice(:key1, :key2, :key3)
  2. hash = other_hash.reject(:key1, :key2, :key3}

Do you mean "other_hash.except(...)"? There is already Hash#reject

#3 [ruby-core:55333] Updated by nobu (Nobuyoshi Nakada) over 4 years ago

  • Description updated (diff)

#4 [ruby-core:55334] Updated by mrkn (Kenta Murata) over 4 years ago

zzak (Zachary Scott) wrote:

Hello,

On Thu, Jun 6, 2013 at 4:12 PM, mrkn (Kenta Murata) muraken@gmail.com wrote:

On Rails, they can be written in the following forms by using ActiveSupport's features.

  1. hash = other_hash.slice(:key1, :key2, :key3)
  2. hash = other_hash.reject(:key1, :key2, :key3}

Do you mean "other_hash.except(...)"? There is already Hash#reject

Yes, It's my mistake!

How can I edit the issue description?

#5 [ruby-core:55335] Updated by mrkn (Kenta Murata) over 4 years ago

  • Description updated (diff)

I could fix the description.

#6 [ruby-core:55336] Updated by nobu (Nobuyoshi Nakada) over 4 years ago

Or enhance the existing methods?

#7 [ruby-core:55338] Updated by mrkn (Kenta Murata) over 4 years ago

nobu (Nobuyoshi Nakada) wrote:

Or enhance the existing methods?

I think Hash#[] with the multiple arguments can be an alternative of Hash#slice.

#9 [ruby-core:55342] Updated by nobu (Nobuyoshi Nakada) over 4 years ago

mrkn (Kenta Murata) wrote:

I think Hash#[] with the multiple arguments can be an alternative of Hash#slice.

Hash#[] should return the values, not the pairs.

#11 [ruby-core:55344] Updated by prijutme4ty (Ilya Vorontsov) over 4 years ago

mrkn (Kenta Murata) wrote:

nobu (Nobuyoshi Nakada) wrote:

Or enhance the existing methods?

I think Hash#[] with the multiple arguments can be an alternative of Hash#slice.

There was a proposal (can't find it) to make indexing by multiple arguments to work as nested hashes so that hsh[:a,:b,:c] works as (hsh[:a] && hsh[:a][:b] && hsh[:a][:b][:c]). Your proposal automatically blocks this proposal.

#12 [ruby-core:55346] Updated by mrkn (Kenta Murata) over 4 years ago

prijutme4ty (Ilya Vorontsov) wrote:

mrkn (Kenta Murata) wrote:

nobu (Nobuyoshi Nakada) wrote:

Or enhance the existing methods?

I think Hash#[] with the multiple arguments can be an alternative of Hash#slice.

There was a proposal (can't find it) to make indexing by multiple arguments to work as nested hashes so that hsh[:a,:b,:c] works as (hsh[:a] && hsh[:a][:b] && hsh[:a][:b][:c]). Your proposal automatically blocks this proposal.

My proposal primaliry consists of Hash#slice, Hash#except, and bang versions of them.
Hash#[] is optional.

#13 [ruby-core:55345] Updated by claytrump (Clay Trump) over 4 years ago

Yay, +1 for slice & except

BTW, makes no sense to me if h[:foo, :bar] returns keys and values in a
hash while h[:foo] returns a value.


<lay trum/>

#15 [ruby-core:55351] Updated by mrkn (Kenta Murata) over 4 years ago

On Fri, Jun 7, 2013 at 1:50 AM, Zachary Scott zachary@zacharyscott.net wrote:

Please update description if the proposal has changed (ie: Hash#[])

I mentioned Hash#[] as the reply to nobu's comment #6.
It has not changed my proposal.
My proposal only consists of slice, slice!, except, and except!.

--
Kenta Murata
OpenPGP FP = 1D69 ADDE 081C 9CC2 2E54 98C1 CEFE 8AFB 6081 B062

#16 [ruby-core:55352] Updated by nobu (Nobuyoshi Nakada) over 4 years ago

I meant Hash#select and Hash#reject by "the existing methods".

i.e.:

hash = other_hash.select(:key1, :key2, :key3)
hash = other_hash.reject(:key1, :key2, :key3)

But Hash#slice and Hash#except seems fine.

#17 [ruby-core:56205] Updated by Glass_saga (Masaki Matsushita) over 4 years ago

How about this implementation?

#18 [ruby-core:56220] Updated by matz (Yukihiro Matsumoto) over 4 years ago

The slice method (Array#slice) retrieve "a slice of elements" from an Array.
Considering that, slice is not a good name for the behavior.

So, I prefer Nobu's idea in comment #16

hash = other_hash.select(:key1, :key2, :key3)
hash = other_hash.reject(:key1, :key2, :key3)

Matz

#19 [ruby-core:56221] Updated by znz (Kazuhiro NISHIYAMA) over 4 years ago

In attached patch.diff

assert_equal({1=>2, 3=>4}, h.slice!(1, 3))

but ActiveSupport's h.slice!(1, 3) returns {5=>6}.

http://api.rubyonrails.org/classes/Hash.html#method-i-slice-21

#20 [ruby-core:56235] Updated by nobu (Nobuyoshi Nakada) over 4 years ago

I've missed the returned values until I've implemented it actually.

  • In ActiveSupport

    • Hash#slice! keeps the given keys and returns removed key/value pairs.
    • Hash#except! removes the given keys and returns self.
  • In this proposal

    • Hash#slice! removes the given keys and returns removed key/value pairs.
    • Hash#except! is same as ActiveSupport.
  • Existing methods

    • Hash#select! keeps the given (by the block) keys and returns self or nil.
    • Hash#reject! removes the given (by the block) keys and returns self or nil.

I don't think changing the result semantics by if any arguments are given is good idea.
What I've thoutht about Hash#slice! was Hash#extract! in ActiveSupport actually.
So what about Hash#extract, Hash#except and those !-versions?

#21 [ruby-core:56242] Updated by mrkn (Kenta Murata) over 4 years ago

The attached file is not a part of my proposal. It made by Glass_saga. My proposal is the same as ActiveSupport.

#22 [ruby-core:56252] Updated by Glass_saga (Masaki Matsushita) over 4 years ago

I'm sorry for my wrong implementation.
patch2.diff makes Hash#slice! and #except! behave the same as ActiveSupport.
However, I think it isn't good idea to import Hash#slice! and #except! from ActiveSupport as it is.

Because:

  • They returns self if no changes were made. It is inconsistent with other built-in methods like #select! and #reject!.
  • #slice! returns rest of hash, not slice of hash like following. It may be confusing.
hash = {1=>2,3=>4,5=>6}
hash.slice!(1,3) #=> {5,6}
hash #=> {1=>2,3=>4}

#23 [ruby-core:60296] Updated by hsbt (Hiroshi SHIBATA) almost 4 years ago

  • Target version changed from 2.1.0 to 2.2.0

#24 [ruby-core:63275] Updated by nobu (Nobuyoshi Nakada) over 3 years ago

  • Description updated (diff)

Another name, Hash#only.
http://blog.s21g.com/articles/228

#25 Updated by ko1 (Koichi Sasada) over 2 years ago

#26 [ruby-core:71381] Updated by keithrbennett (Keith Bennett) about 2 years ago

I was about to report this feature request but was happy to find that it was already there...but disappointed that it's been here so long. I'm using Ruby but not Rails and have been needing this functionality a lot lately.

Unless I'm misunderstanding, the implementation of such methods would be trivial. I would much rather see this implemented with a nonoptimal name than not implemented at all.

'slice' and 'except' are ok with me, but if there is a concern about their correctness as names, perhaps they could be 'select_if_key_in' and 'reject_if_key_in'.

But again, anything would be better than nothing.

#27 Updated by shyouhei (Shyouhei Urabe) over 1 year ago

#28 Updated by mrkn (Kenta Murata) 4 months ago

#29 [ruby-core:82698] Updated by Glass_saga (Masaki Matsushita) 3 months ago

I just rebased it to trunk.

#30 [ruby-core:82860] Updated by Glass_saga (Masaki Matsushita) 3 months ago

Improved implementation and test.
Added documentation.

#31 [ruby-core:83381] Updated by knu (Akinori MUSHA) about 2 months ago

One concern is that Array#slice! returns deleted elements whereas this Hash#slice! would return pairs left after slicing.

#32 [ruby-core:83382] Updated by mrkn (Kenta Murata) about 2 months ago

  • Status changed from Assigned to Closed

As Hash#slice has been accepted in #13563, I decided to close this issue.
I intend to open the different issue for discussing other methods such as Hash#except.

Thanks.

Also available in: Atom PDF