【Ruby入門】Array#assocとArray#rassocで配列をハッシュのように扱う方法
配列に大量のデータを格納しているけれど、ハッシュのようにキーと値のペアで検索したい——そんな経験はありませんか? 実はRubyには、配列をキー/バリュー構造のように扱える便利な仕組みが用意されています。この記事では、その使い方をわかりやすく解説します。
Array#assoc と Array#rassoc とは
ここでは、数分ごとに「買え」「売れ」という銘柄推奨を出してくれる、架空の株予想マシンを例に考えてみましょう。このマシンをコンピュータに接続すると、次のようなデータのストリームを受け取れるとします。
picks = [
["AAPL", "buy"],
["GOOG", "sell"],
["MSFT", "sell"]
]
Google(GOOG)に関する最新の推奨を取得したい場合は、Array#assoc メソッドを使います。各行の先頭要素(row[0])が指定したキーと一致する最初の行を返してくれます。
# row[0] == "GOOG" となる最初の行を返す
picks.assoc("GOOG") # => ["GOOG", "sell"]
逆に、「sell」という推奨が含まれる行を探したい場合は Array#rassoc メソッドを使います。こちらは各行の2番目の要素(row[1])と照合します。
# row[1] == "sell" となる最初の行を返す
picks.rassoc("sell") # => ["GOOG", "sell"]
該当する行が見つからなかった場合、どちらのメソッドも nil を返します。
picks.assoc("CSCO") # => nil
picks.rassoc("hold") # => nil
履歴データを残しながら最新の値を取得する
ハッシュには「同じキーに対して1つの値しか持てない」という制約があります。一方、配列なら重複したキーをいくらでも持てます。assoc / rassoc は賢いことに、複数マッチした場合でも最初に見つかった行だけを返します。これを利用すると、かなり面白いことができます。
例の株予想マシンは、時間が経つと判断を覆すことがあります。以前「売れ」と言っていた銘柄を、後になって「買え」と推奨し直すかもしれません。その場合、データは次のようになります。
picks = [
["GOOG", "buy"],
["AAPL", "sell"],
["AAPL", "buy"],
["GOOG", "sell"],
["MSFT", "sell"]
]
もしハッシュにこのデータを放り込むと、特定の銘柄の推奨を更新した時点で、それまでの履歴は失われてしまいます。しかし配列なら話は別です。新しい推奨を配列の先頭に追加していけば、Array#assoc は常に最新の推奨を返してくれるからです。
# row[0] == "GOOG" となる最初の行=最新の推奨を返す
picks.assoc("GOOG") # => ["GOOG", "buy"]
つまり、ハッシュのようなキー/バリュー検索の手軽さに加えて、監査ログ(変更履歴)まで無料で手に入るわけです。
3列以上のデータにも対応
assoc のもう一つの魅力は、各行のカラム数が2つに限定されない点です。何列でも自由に持たせられます。たとえば、売買の推奨にタイムスタンプを付けてみましょう。
picks = [
["AAPL", "buy", "2015-08-17 12:11:55 -0700"],
["GOOG", "sell", "2015-08-17 12:10:00 -0700"],
["MSFT", "sell", "2015-08-17 12:09:00 -0700"]
]
この状態で assoc や rassoc を使うと、タイムスタンプも含めた行全体が返ってきます。
# 行全体が返される
picks.assoc("GOOG") # => ["GOOG", "sell", "2015-08-17 12:10:00 -0700"]
CSVなど、多数のカラムを持つファイル形式のデータを扱う際に、この挙動がどれほど役立つか想像できるでしょう。
パフォーマンス:ハッシュとの速度比較
ほとんどのベンチマークにおいて、Rubyのハッシュは Array#assoc より高速です。データセットが大きくなるほど差は顕著になります。ハッシュテーブルの検索計算量は O(1) であるのに対し、配列の線形検索は O(n) だからです。ただし、ケースによってはその差が実用上問題にならないことも多く、実際の判断はデータの規模やアクセスパターン次第です。
参考までに、10行の小さなデータセットと10万行の大きなデータセットで、ハッシュ検索と assoc の速度を比較する簡単なベンチマークを実行してみました。結果は予想通り、小さいデータセットでは両者とも良好な性能を示しましたが、大きいデータセットではハッシュが圧勝しました。
…とはいえ公平を期すために言うと、このベンチマークでは配列の最後尾の要素を検索しています。これは配列検索にとって最悪のケースであり、実測値は悲観的に見積もられています。
require 'benchmark/ips'
require 'securerandom'
Benchmark.ips do |x|
x.time = 5
x.warmup = 2
short_array = (0..10).map { |i| [SecureRandom.hex(), i] }
short_hash = Hash[short_array]
short_key = short_array.last.first
long_array = (0..100_000).map { |i| [SecureRandom.hex(), i] }
long_hash = Hash[long_array]
long_key = short_array.last.first
x.report("short_array") { short_array.assoc(short_key) }
x.report("short_hash") { short_hash[short_key] }
x.report("long_array") { long_array.assoc(long_key) }
x.report("long_hash") { long_hash[long_key] }
x.compare!
end
# Calculating -------------------------------------
# short_array 91.882k i/100ms
# short_hash 149.430k i/100ms
# long_array 19.000 i/100ms
# long_hash 152.086k i/100ms
# -------------------------------------------------
# short_array 1.828M (± 3.4%) i/s - 9.188M
# short_hash 6.500M (± 4.8%) i/s - 32.426M
# long_array 205.416 (± 3.9%) i/s - 1.026k
# long_hash 6.974M (± 4.2%) i/s - 34.828M
# Comparison:
# long_hash: 6974073.6 i/s
# short_hash: 6500207.2 i/s - 1.07x slower
# short_array: 1827628.6 i/s - 3.82x slower
# long_array: 205.4 i/s - 33950.98x slower
まとめ
Array#assoc:各行の先頭要素をキーとして検索し、一致した最初の行を返すArray#rassoc:各行の2番目の要素をキーとして検索し、一致した最初の行を返す- 見つからない場合は
nilを返す - 重複キーを許容するため、履歴を保持しながら最新値を取得できる
- カラム数の制限がないため、CSVなどの多列データとの相性が良い
- 大規模データではハッシュの方が高速なので、用途に応じて使い分けるのが賢明
小〜中規模のデータや履歴管理が必要な場面では、assoc / rassoc はハッシュでは実現できない柔軟性を提供してくれます。ぜひ活用してみてください。
-
Ruby 2.6のMJITとは?仕組みとパフォーマンスを徹底解説
Rubyのパフォーマンスは、バージョンが上がるごとに大きく向上してきました。そしてRuby開発チームは、さらに速いRubyを実現するために全力を尽くしています。 その取り組みのひとつが「3×3プロジェクト」です。 その目標とは? Ruby 3.0を、Ruby 2.0の3倍速くすることです。 このプロジェクトの一環として誕生したのが、新しいMJITコンパイラです。これが本記事のテーマとなります。 MJITとは何か MJITは「Method Based Just-in-Time Compiler(メソッドベースのジャストインタイムコンパイラ)」の略称です。 これは具体的にどういう意味でしょうか?
-
Ruby on Railsとは?初心者にもわかる仕組み・魅力・学び方を徹底解説
Ruby on Railsとは? Ruby on Rails(略称:RoR)は、世界で最も人気のあるオープンソースのWebアプリケーションフレームワークです。プログラミング言語「Ruby」をベースに構築されており、シンプルなサイトから大規模で複雑なサービスまで、幅広いWebアプリケーションの開発を支援します。 そもそもフレームワークとは? フレームワークとは、ソフトウェア開発の際に土台となる構造を提供してくれるコードやツール、ユーティリティの集合体です。あらかじめ用意された構造に沿ってコードを書くことで、プログラムが整理され、保守性も高まります。正しく使いこなせるようになれば、開発作業は格段に