バイオインフォ道場、くまぞうです。
ハッシュは、複数の値を管理する入れ物のようなものです。データをキーとバリューで管理します。参照や編集・追加・削除などを行うことができます。繰り返し処理で発生したデータをキー(名前や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情報を取り出して表示しています。