mikanmarusanのブログ

テクノロジーとかダイビングとか

ハッシュスライス(Hash#slice)

2017/12/25にRuby2.5.0がリリースされた。 Ruby2.5.0 から 待望の Hash#slice が 標準メソッドになったのを記念してエントリを書いている。

ハッシュスライス

ハッシュスライスとは、指定したキーに合致するキーと要素からなる、新しいハッシュを作ることである。

Rails用のライブラリを提供しているので、Railsでアプリケーションをサンプルでいくつか書くのだけれども、ハッシュから特定のキーだけを持つハッシュを作ることがある。このような場合にはハッシュスライスが便利。例えば Rails の Controller において リクエスト情報(クエリパラメータやPOSTで送られてくるデータ)が格納される params について使いたいキーのみ残して引き回したいときなどだ。

Railsではこれが簡単にできる。
Railsのコア拡張(ActiveSupport::CoreExtenstions::Hash::Slice)のおかげで

{ a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
# => {:a=>1, :c=>3}

な風に描ける、便利すぎて泣ける。
Railsは本当にエンジニアをダメにしてくれる素晴らしいメソッドを用意してくれているとよく思う。
これを 標準Ruby で実行すると、Ruby2.5.0以前であれば、アレ動かない?となる。

ちなみに irb上記メソッドを実行してみればわかるが、NoMethodErrorで怒られる。

% irb          
irb(main):001:0> { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
NoMethodError: undefined method `slice' for {:a=>1, :b=>2, :c=>3, :d=>4}:Hash
    from (irb):1
    from /usr/bin/irb:12:in `<main>'

じゃRuby2.5.0以前はどのようにやればいいかというと、 selectメソッドを組み合わせて下記のように記述することが(少なくとも自分は)多かった。ただし直感的じゃないので1週間後ぐらいにリファクタリングする自分に自信が持てない、つらみ。

sliced = { a: 1, b: 2, c: 3, d: 4 }.select { |k,_| [:a, :c].include? k }
# => {:a=>1, :c=>3}

ということでRuby2.5.0でのHash::sliceを試してみる。

Ruby 2.5.0 の インストール

念のためインストール手順の記載。rbenvで楽勝。

# rubyのバージョンを確認する、古い
$ ruby -v
ruby 2.3.1p112 (2016-04-26) [x86_64-linux-gnu]
# rbenvでインストール可能なバージョンを調べる、2.5.0は大丈夫そう
$ rbenv install --list | grep 2.5.0
  2.5.0-dev
  2.5.0-preview1
  2.5.0-rc1
  2.5.0
  rbx-2.5.0
# ruby 2.5.0のインストール
$ rbenv install -v 2.5.0
# インストールできたかを確認し、ruby 2.5.0 に切り替える。globalが怖い場合はlocalでも可
$ rbenv versions
* system (set by /home/ubuntu/.rbenv/version)
  2.5.0
$ rbenv global 2.5.0
$ rbenv versions
  system
* 2.5.0 (set by /home/ubuntu/.rbenv/version)
# rubyのバージョン確認をする、できた
$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]

Hash#sliceをためしてみる

Hash#sliceでシンプルに記載できるようになる。

# 対象のハッシュ
hash = {foo: 1, bar: 2, baz: 3}

# :fooと:bazのみを取り出したい
sliced = hash.slice(:foo, :baz)
pp sliced
# [実行結果]
{:foo=>1, :baz=>3}

これで JSON とか YAML のデータを読み込んでハッシュで保持しているときに、必要なキーのデータだけにするとかが簡単に出来そうで素敵。

(番外編)perl のハッシュスライス

perl には以前からハッシュスライスは存在していて、特定のキーの値をリスト形式で取得できるハッシュスライス @hash{... }; が有名であるが、キーと値のペアではないので、 RubyHash#slice とは結果が異なる。

my %hash = (foo => 1, bar => 2, baz => 3);
my($foo, $baz) = @h{qw/foo baz/};

perl 5.20 からは 新たなスライス表記として、キーと値のペアを戻す「%hash{…}」というスライス表記が加わっているので、RubyHash#slice と同じ結果となる。

#!/usr/bin/perl

use strict;
use warnings;

my %hash = (foo => 1, bar => 2, baz => 3);

# キーと値のペアを戻すハッシュスライス%hash{…}
my %sliced =  %hash{('foo', 'baz')};

perlでもrubyでもハッシュスライスができて万歳。

まとめ

Ruby 2.5.0 からの Hash#slice はイイ。