RubyとPythonのHash(Dictionary)操作対応まとめ

# 概要

RubyのArray、PythonのHash (Dictionary) の典型的な操作のまとめ。

Python(Ruby)を学習し始めたばかりのRubyist(Pythonista)用の記事です。

# 前提条件

- Python 3.5.2
- Ruby 2.3.1

## 参考情報

- Ruby
    - https://docs.ruby-lang.org/ja/latest/class/Hash.html
- Python
    - http://docs.python.jp/3/tutorial/datastructures.html#dictionaries
    - http://www.pythonweb.jp/tutorial/dictionary/

# メソッド

## 初期化

- ruby

```rb
h = {"a" => 1, "b" => 2, "c" => 3}
h2 = {a: 1, b: 2, c: 3}   # symbol をkeyにする場合
```

- python

```py
d = {'a': 1, 'b': 2, 'c': 3}
```

## 要素の取り出し

- ruby

```rb
h['c']    #=> 3
h['d']    #=> nil
```

```py
d['c']    #=> 3
d['d']    #=> 例外KeyErrorが発生
```

## size

- ruby

```rb
h.size   #=> 3
```

- python

```py
len( d )   #=> 3
```

## each

要素に対してループを回す

- ruby

```rb
h.each do |key,val|
  puts key,val
end
```

- python

```py3
for k,v in d.items():
  print(k,v)
```

## keys, values

key,valueのリストを取得する

- ruby

```rb
h.keys    #=> ["a", "b", "c"]
h.values  #=> [1, 2, 3]
```

- python
  - Python3ではdict_keys,dict_valuesオブジェクトが返ってくる
    - http://www.atmarkit.co.jp/ait/articles/0902/06/news139.html
    - dictionaryへの参照を返しているので、dictionary自体を変更するとkeys自体も変わる
  - listが欲しい場合は `list()`を使う。

```py3
keys = d.keys()       #=> dict_keys(['b', 'a', 'c'])
values = d.values()   #=> dict_values([2, 1, 3])

d['d'] = 4
keys                     #=> dict_keys(['b', 'd', 'a', 'c'])
values                   #=> dict_values([2, 4, 1, 3])

list(keys)               #=> ['b', 'd', 'a', 'c']
```

## 配列から生成

- ruby
    - `Array#to_h` を使うのが楽

```rb
a = [1,2,3,4]
a.map {|x| [x,x+3] }.to_h      #=> {1=>4, 2=>5, 3=>6, 4=>7}
```

- python
    - リスト内包表記

```py3
a = [1,2,3,4]
{ i:i+3 for i in a }     #=> {1: 4, 2: 5, 3: 6, 4: 7}
```

## key,valueのmap

- ruby
    - keyやvalueを変換したい場合、mapを使う。map自体はArrayのArrayを返すので、それを`Hash[]`でHashに変換する

```rb
h = {"a" => 1, "b" => 2, "c" => 3}
h.map {|k,v| [k.upcase, -v] }            #=> [["A", -1], ["B", -2], ["C", -3]]
h.map {|k,v| [k.upcase, -v] }.to_h       #=> {"A"=>-1, "B"=>-2, "C"=>-3}
```

- python

```py3
d = {'a': 1, 'b': 2, 'c': 3}
{ k.upper():-v for k,v in d.items() }    #=> {'A': -1, 'B': -2, 'C': -3}
```

## has_key?

- ruby

```rb
h = {"a" => 1, "b" => 2, "c" => 3}
h.has_key?("a")               #=> true
h.has_key?("d")               #=> false
```

- python
    - python2までは `d.has_key('a')` というメソッドも使えたらしい。

```py3
d = {'a': 1, 'b': 2, 'c': 3}
'a' in d                      #=> True
'd' in d                      #=> False
```

## merge

- ruby
    - `Hash#merge`を使う。非破壊的なメソッド。破壊的なメソッドは`merge!`
    - 同じキーがあった場合、デフォルトでは引数の値が優先される。blockを渡せば指定可能
        - https://docs.ruby-lang.org/ja/latest/method/Hash/i/merge.html

```rb
h1 = {"a" => 1, "b" => 2, "c" => 3}
h2 = {"d" => 4, "e" => 5, "f" => 6}
h1.merge(h2)
# => {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6}
```

- python
    - `update`というメソッドがあるが、破壊的な操作...

```py3
d1 = {"a": 1, "b": 2, "c": 3}
d2 = {"d": 4, "e": 5, "f": 6}
d1.update(d2)
d1
#=> {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
```

## delete

- ruby

```rb
h = {"a" => 1, "b" => 2, "c" => 3}
h.delete('a')                      #=> 1
h                                  #=> {"b"=>2, "c"=>3}
```

- python

```py3
d = {"a": 1, "b": 2, "c": 3}
d.pop('a')                         #=> 1
d                                  #=> {'b': 2, 'c': 3}
```

## デフォルト値の設定

ヒストグラムをとったりする場合に連想配列を使うケース

- ruby

```rb
chars = ["a", "b", "b", "c", "c", "c"]
h = Hash.new(0)
chars.each {|c| h[c] += 1 }            # 文字数のカウント
h                                      #=> {"a"=>1, "b"=>2, "c"=>3}
```

- python
    - http://www.lifewithpython.com/2015/05/python-dict-default-value.html を参考に
    - `default_dict` を使う方法もある

```py3
chars = ["a", "b", "b", "c", "c", "c"]
d = {}
for c in chars:
  d[c] = d.get(c,0) + 1             # 値があれば取得し、なければ0を返す
d                                   #=> {'a': 1, 'b': 2, 'c': 3}
```

```py3
from collections import defaultdict
chars = ["a", "b", "b", "c", "c", "c"]
d = defaultdict( lambda: 0 )
for c in chars:
  d[c] += 1

d
# => defaultdict(<function __main__.<lambda>>, {'a': 1, 'b': 2, 'c': 3})
```

### 配列のindexを作る場合

- ruby
    - 注意点 `Hash.new( [] )` とするとバグる
    - https://docs.ruby-lang.org/ja/latest/method/Hash/s/new.html

```rb
words = ["a1", "b1", "b2", "c1", "c2", "c3"]
h = Hash.new {|hash,key| hash[key] = [] }
words.each {|w| h[ w[0] ] << w }
h
# => {"a"=>["a1"], "b"=>["b1", "b2"], "c"=>["c1", "c2", "c3"]}
```

- python

```py3
words = ["a1", "b1", "b2", "c1", "c2", "c3"]
d = defaultdict( list )
for w in words:
  key = w[0]
  d[key].append(w)

d
#=> defaultdict(list, {'a': ['a1'], 'b': ['b1', 'b2'], 'c': ['c1', 'c2', 'c3']})
```