Ruby

コレクション

Ruby

Array・Hash・Range・Enumerable

Array

作成・操作・Enumerable メソッド

arrays.rb ruby
# 作成
arr = [1, 2, 3, 4, 5]
arr = Array.new(5, 0)        # [0,0,0,0,0]
arr = Array.new(5) { |i| i * 2 }  # [0,2,4,6,8]
arr = %w[apple banana cherry]      # ['apple','banana','cherry']
arr = %i[foo bar baz]              # [:foo,:bar,:baz]

# アクセス
arr[0]        # 最初
arr[-1]       # 最後
arr[1..3]     # [2,3,4] (範囲)
arr[1, 2]     # [2,3] (start, length)
arr.first     # 最初の要素
arr.first(3)  # 最初の3要素
arr.last      # 最後の要素
arr.sample    # ランダムに1つ
arr.sample(3) # ランダムに3つ

# 追加・削除
arr.push(6)           # 末尾追加
arr << 7              # 末尾追加(<<演算子)
arr.unshift(0)        # 先頭追加
arr.pop               # 末尾削除・返却
arr.shift             # 先頭削除・返却
arr.delete(3)         # 値で削除(全て)
arr.delete_at(0)      # インデックスで削除
arr.compact           # nil を除いた新配列
arr.flatten           # ネストを平坦化
arr.flatten(1)        # 1段階だけ平坦化
arr.uniq              # 重複排除

# 変換
arr.map  { |x| x * 2 }       # 変換(collect も同義)
arr.select { |x| x.even? }   # 絞り込み(filter も同義)
arr.reject { |x| x.even? }   # 除外
arr.reduce(0) { |sum, x| sum + x }  # 畳み込み(inject も同義)
arr.reduce(:+)                # シンボルで演算子を指定
arr.each_with_object([]) { |x, memo| memo << x * 2 }

arr.flat_map { |x| [x, x * 2] } # map + flatten(1)
arr.zip([10,20,30])            # [[1,10],[2,20],[3,30]]
arr.each_slice(2).to_a         # [[1,2],[3,4],[5]]
arr.each_cons(3).to_a          # [[1,2,3],[2,3,4],[3,4,5]]
arr.group_by { |x| x % 3 }    # {1=>[1,4], 2=>[2,5], 0=>[3]}
arr.tally                      # 出現回数 {1=>1,2=>1,...}

arr.sort                       # 昇順
arr.sort_by { |x| -x }        # カスタムソート(降順)
arr.min; arr.max
arr.min_by { |x| x.abs }; arr.max_by { |x| x.abs }
arr.sum; arr.count; arr.any? { |x| x > 3 }; arr.all? { |x| x > 0 }

Hash

作成・操作・変換・マージ

hashes.rb ruby
# 作成
hash = { name: 'Alice', age: 30 }       # シンボルキー(推奨)
hash = { 'name' => 'Alice', 1 => :one } # ハッシュロケット(文字列・数値キー)
hash = Hash.new(0)                      # デフォルト値付き

# アクセス
hash[:name]               # 'Alice'
hash[:missing]            # nil(KeyError は出ない)
hash.fetch(:name)         # 'Alice'
hash.fetch(:missing)      # KeyError!
hash.fetch(:missing, 'N/A')           # デフォルト値
hash.fetch(:missing) { |k| "#{k} not found" }  # ブロック

# 更新
hash[:email] = 'alice@example.com'  # 追加・更新
hash.merge!(age: 31)                # 破壊的マージ
hash.delete(:email)                 # 削除して返却
hash.reject! { |k, v| v.nil? }     # 条件で削除

# 確認
hash.key?(:name)     # true(has_key? も同義)
hash.value?('Alice') # true(has_value? も同義)
hash.any? { |k, v| v.is_a?(Integer) }
hash.all? { |k, v| v }
hash.count { |k, v| v.is_a?(String) }

# イテレーション
hash.each { |key, val| puts "#{key}: #{val}" }
hash.each_with_object({}) { |(k, v), memo| memo[k] = v.to_s }

# 変換
hash.keys          # [:name, :age]
hash.values        # ['Alice', 30]
hash.to_a          # [[:name,'Alice'],[:age,30]]
hash.map { |k, v| [k, v.to_s] }.to_h
hash.transform_keys(&:to_s)       # キーを変換
hash.transform_values(&:to_s)     # 値を変換
hash.filter_map { |k, v| [k, v * 2] if v.is_a?(Integer) }
hash.select { |k, v| v.is_a?(String) }  # 絞り込み
hash.reject { |k, v| v.nil? }           # 除外

# マージ
defaults = { role: 'user', active: true }
override = { role: 'admin' }
merged = defaults.merge(override)        # overrideが優先
merged = defaults.merge(override) { |key, old, new_val| new_val } # ブロックで解決

Range と Enumerable

範囲オブジェクトと便利なメソッド集

range_enumerable.rb ruby
# Range
(1..10)          # 1から10まで(10を含む)
(1...10)         # 1から9まで(10を含まない)
('a'..'z')       # 文字の範囲
(1.0..3.0)       # 浮動小数点

(1..10).to_a     # [1,2,3,...,10]
(1..10).sum      # 55
(1..10).include?(5)  # true
(1..10).cover?(5)    # true(数値の範囲に対して高速)

# 範囲と case
case age
when 0..17  then '未成年'
when 18..64 then '成人'
when 65..   then '高齢者'  # 下限のみの範囲
end

# Enumerable モジュールが提供するメソッド
nums = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]

nums.find   { |x| x > 4 }      # 5(最初の1つ)
nums.count  { |x| x > 4 }      # 4
nums.sum    { |x| x * 2 }      # 78
nums.minmax                     # [1, 9]
nums.minmax_by { |x| -x }      # [9, 1]

nums.each_with_index { |v, i| puts "#{i}: #{v}" }
nums.map.with_index  { |v, i| "#{i}:#{v}" }

nums.chunk { |x| x > 3 }.to_a
# [[false,[3,1]],[true,[4]],[false,[1]],[true,[5,9]],...]  

nums.lazy.select { |x| x.odd? }.map { |x| x**2 }.first(3)
# 遅延評価: 必要な分だけ処理

# Comparable モジュール
class Temperature
  include Comparable
  attr_reader :degrees
  def initialize(d) = (@degrees = d)
  def <=>(other) = degrees <=> other.degrees
end

t1 = Temperature.new(20)
t2 = Temperature.new(30)
t1 < t2   # true
[t2, t1].sort  # [t1, t2](昇順)
t1.clamp(Temperature.new(15), Temperature.new(25))  # t1