Ruby hash ハッシュの基本と応用 スクリプトの書き方

バイオインフォ道場、くまぞうです。

ハッシュは、複数の値を管理する入れ物のようなものです。データをキーとバリューで管理します。参照や編集・追加・削除などを行うことができます。繰り返し処理で発生したデータをキー(名前やIDなど)で1つずつ保存したり、必要になったときに対応するキーでデータを読み出したりするような使い方をします。Hashは、その構造からマップと呼ばれたり、連想配列と呼ばれたりします。

スポンサーリンク



ハッシュの基本的な使い方

ハッシュは、データをキー・バリューで管理します。配列のように格納した順番ではなく、名前やIDなどのキー(オブジェクト)で管理します。追加したデータは、キーを使って参照・編集・追加・削除などを行うことができます。配列は、順番を覚えておく必要がありますが、ハッシュの場合はキーを工夫することで無駄なく効率的にデータ管理できます。それぞれの特長を活かしたデータ管理ができるように工夫しましょう。

生成

ハッシュの生成は簡単です。空のハッシュを作ることもできるし、予めデータがセットされたハッシュを作ることもできます。尚、ハッシュのキーは、文字列よりもSymbolの方が効率的です。

  aaa = {} #空のハッシュ
  bbb = {"AAA"=>111, 123=>222, "CCC"=>333}
  bbb = {:AAA=>111, 123=>222, :CCC=>333}  # symbolを利用
  ccc = Hash.new  # 空のハッシュ。生成に細かい操作を加えたい場合に使います。

参照

ハッシュを参照する場合は、キーでアクセスします。キーだけ、バリューだけの参照も可能です。

  bbb = {"AAA"=>111, 123=>222, "CCC"=>333}
  puts bbb["AAA"]               # 111
  puts bbb[123]                 # 222
  puts bbb.keys                 # ["AAA", 123, "CCC"]
  puts bbb.values               # [111, 222, 333]
  puts bbb["DDD"]               # 対応するバリューがなければnilが返される

編集

ハッシュ要素は書き換えることが出来ます。キーを指定して新しいデータを代入すると、指定したデータを更新します。

  bbb = {"AAA"=>111, 123=>222, "CCC"=>333}
  puts bbb["AAA"]               # 111
  bbb["AAA"] = 0
  puts bbb                      # {"AAA"=>0, 123=>222, "CCC"=>333}

追加

新しいキーを指定してデータを追加します。

  bbb = {"AAA"=>111, 123=>222, "CCC"=>333}
  bbb["DDD"] = 444
  puts bbb  # {"AAA"=>111, 123=>222, "CCC"=>333, "DDD"=>444}

削除

Hashからデータを削除します。

  bbb = {"AAA"=>111, 123=>222, "CCC"=>333}
  bbb.delete 123 
  puts bbb                      # {"AAA"=>111, "CCC"=>333}

Hashの便利な使い方

基本的な操作以外にソートやデータ検索などの操作を簡単に行えるように、たくさんのメソッドが提供されています。よく使いそうなものを紹介します。

ソート

キーによるソート、バリューによるソートなどを行うことが出来ます。尚、ソート後のデータは配列として取得します。

  bbb = {"AAA"=>111, "BBB"=>100, "CCC"=>333}
  bbb.sort                      # [["AAA", 111], ["BBB", 100], ["CCC", 333]]
  bbb.sort{|a,b| a[1]<=>b[1]}   # value sort
                                # [["BBB", 100], ["AAA", 111], ["CCC", 333]]

要素の列挙

要素を1つ1つ列挙します。forやwhileに似たイテレーターを使います。

  puts bbb                      # {"AAA"=>111, 123=>222, "CCC"=>333}
  aaa.each {|pair| print pair}  # ["AAA", 111][123, 222]["CCC", 333]
  aaa.each {|k,v| puts "key=#{k}, value=#{v}"}
                                # key=AAA, value=111
                                # key=123, value=222
                                # key=CCC, value=333

要素の検索

要素を検索します。存在をtrue/falseで返したり、対応するキーを返したりします。

  puts bbb                      # {"AAA"=>111, 123=>222, "CCC"=>333}
  bbb.include?("AAA")           # true : キーに"AAA"を含むか?
  bbb.value?(111)               # true : バリューに111を含むか?
  bbb.key(111)                  # "AAA" : 対応するキーを検索

配列へ変換

Hashから配列へ変換します。

  puts bbb                      # {"AAA"=>111, 123=>222, "CCC"=>333}
  bbb.to_a                      # [["AAA", 111], [123, 222], ["CCC", 333]]
  bbb.to_a[0]                   # ["AAA", 111]
  bbb.to_a[0][1]                # 111

プログラム

test_hash.rb
#! /usr/bin/ruby

class MyData
  attr :name
  attr :seq
  @ha_data = {} # hash
  @ha_sort = {} # hash

  def initialize(entry)
    entry =~ /\n/
    @name, @seq = [$`,$'].map &:strip
  end

  def self.new(entry)
    obj = super
    @ha_data[obj.name] = obj.seq
    @ha_sort[obj.name] = obj.seq.length
  end

  def self.order
    ordered = @ha_sort.values.sort.map {|x| @ha_sort.key(x)}
    ordered.each {|key| puts key + "\n" + @ha_data[key]}
  end
end

sep = $/; $/ = '>'
ents = {}
File.open(ARGV[0]).each do |entry|
  next if entry == ">"
  MyData.new(entry.chomp)
end
$/ = sep

MyData.order

プログラム実行

test_data.fasta
>entry1(length=8)
AAAAA
AAA
>entry2(length=6)
BBB
BBB
>entry3(length=7)
CCC
CCCC
test_hash
$ test_hash.rb test_data.fasta
>entry2(length=6)
BBB
BBB
>entry3(length=7)
CCC
CCCC
>entry1(length=8)
AAAAA
AAA

プログラムについて

test_hash.rbでは、ハッシュの使用例としてFASTA形式のファイルを長さ順に並び替えるプログラムを作りました。セパレーターを調整して(sep = $/; $/ = ‘>’…最後に戻します。$/ = sep)、ファイルからエントリー毎に読み出し、MyDataクラスに格納します。MyDataはいくつか生成されますが、生成データをクラス変数で一貫して管理しました(@ha_data[obj.name] = obj.seq)。同時に長さも管理(@ha_sort[obj.name] = obj.seq.length)し、最後のMyData.orderクラスメソッドで、長さ順に並べた名前をキーにしてseq情報を取り出して表示しています。

スポンサーリンク