やったもん勝ち

主にプログラミングのこと。生産性向上の某とかも。

rubyで配列、ハッシュに対する処理をするreduceについてのまとめ備忘録

reduceとは

reduceはinjectの別名。

配列やハッシュを次々に処理していくときに使えるやつ。

早速例示して使ってみる。

まず配列について

# 初期値なしで、配列の中身を順に足していく。
[1,2,3,4,5].reduce() {|sum, i| sum + i }
15
# 初期値を設定できる
[1,2,3,4,5].reduce(100) {|sum, i| sum + i }
115
# 偶数なら足す
# 初期値なし
[1,2,3,4,5].reduce() {|sum, i|
  puts sum
  if i % 2 == 0
    sum + i
    else
    sum
  end}
1
3
3
7

7

パッと書いてみたが、期待する結果じゃない。 これの挙動を確かめる。

①初期値がないので、[sum, i] = [1, 2]になる。
i=2なので、ifはtrueになり、sum+iが実行される。
よって返り値は3

②Z1周目のループが終わって次のループ。
[sum, i] = [3, 3]になる。
i=3なので、ifはfalseになり、sum(=3)が返される。

③3週目のループ
[sum, i] = [3, 4]になる。
i=4なので、ifはtrueになり、sum+i(=3+4=7)が返される。

④4週目のループ
[sum, i] = [7, 5]になる。
i=5なので、ifはfalseになり、sum(=7)が返される。

初期値がないと、こういった期待しない結果が返ってくる罠に遭遇しそう。
なので、初期値はなるべく都度適切なものを入れたほうがよい気がしている。

# 偶数なら足す
# 初期値を0とする
[1,2,3,4,5].reduce(0) {|sum, i|
  if i % 2 == 0
    sum + i 
    else
    sum
  end}
6
# 1行で書く熟練っぽいコードはまだ慣れない…
[1,2,3,4,5].reduce(0) {|sum, i|if i % 2 == 0 then sum + i else sum end}
6
# 偶数なら足す
# 初期値は0とする
# elseの処理も書かないと、前回の戻り値がないことになってしまう。
[1,2,3,4,5].reduce(0) {|sum, i|
  if i % 2 == 0
    sum + i
  end}
NoMethodError: undefined method `+' for nil:NilClass

ハッシュについて

# ハッシュのvalueだけを利用する
fruit = { apple: 100, banana: 200, orange: 300, lemon: 400 }
fruit.reduce(0) {|sum, (key, value)| sum += value}
1000
# ハッシュのvalueを条件にする
# 初期値には空の配列[]を使用する
fruit.reduce([]) {|array, (key, value)| array.push(key)}
[:apple, :banana, :orange, :lemon]
# reduceは毎回返り値を持たせないとエラーになる罠がある。
fruit.reduce([]) {|array, (key, value)| array.push(key)if value > 250 }
SyntaxError: <main>:1: syntax error, unexpected tIDENTIFIER, expecting '}'
ray.push(key)if value > 250 }
                              ^
# 返り値がないときは、とりあえず何か強引に返しとく
# 最後の値が返り値になる
#ちゃんと書くならこう
fruit.reduce([]) {|array, (key, value)| 
  if value > 250
    array.push(key)
  else
    array
  end}
[:orange, :lemon]
# 短く書くならこう
fruit.reduce([]) {|array, (key, value)| array.push(key)if value > 250; array}
[:orange, :lemon]

mapよりは若干複雑かな