かずおの開発ブログ(主にRuby)

日々の開発のことを色々書きます。

配列、ハッシュを半分に分ける

Rubyを書いていて配列やハッシュを綺麗に半分に分けたいなってときありますよね?少なくともぼくはあったし、今この記事を見ているあなたもそう思ってるのかもしれません。 イメージとしてはこんな感じです。

[1, 2, 3, 4] => [[1, 2], [3, 4]]
["Yamada" => 34, "Katou" => 28, "Endou" => 18, "Matsumoto" => 25] 
=> ["first_half" => ["Yamada" => 34, "Katou" => 28],  "second_half"  =>["Endou" => 18, "Matsumoto" => 25]]

配列

まず配列についてですが、もっといいやり方がある気もするのですが、単純な方法で実装しています。...と..

今回は要素が偶数でちょうど半分に割ることを想定しているので、奇数の場合はfirst_halfの要素が1つ多くなってしまいますが、そこはオプションを持たしたりということはしていません。

class HalfArray
  def self.half_array(array)
    first_half   = array.slice(0...array.size/2)
    second_half  = array.slice((array.size/2)..array.size)
    halfed_array = [first_half, second_half]
  end
end
sample_array = [1, 2, 3, 4]
HalfArray.half_array(sample_array) => [[1, 2], [3, 4]]

でせっかくなのでArrayクラスを拡張した形に変えてみます。

class Array
  def half
    first_half   = self.slice(0...self.size/2)
    second_half  = self.slice((self.size/2)..self.size)
    halfed_array = [first_half, second_half]
  end
end

sample = [1,2,3,4]
sample.half => [[1, 2], [3, 4]]

うまくいきましたね!

ハッシュ

続いてハッシュです。これについては一旦Arrayにして半分に割りもう一度ハッシュに戻すという、配列以上にもっといいやり方がありそうな気がするやり方をしてます笑

class HalfHash
  def self.half_hash(hash, first_key, second_key)
    first_half   = Hash[hash.to_a.slice(0...(hash.size/2))]
    second_half  = Hash[hash.to_a.slice((hash.size/2)..hash.size)]
    halfed_hash = {first_key => first_half, second_key => second_half}
  end
end

sample_hash = {"Yamada" => 34, "Katou" => 28, "Endou" => 18, "Matsumoto" => 25}
result = HalfHash.half_hash(sample_hash, "sample_first", "second_first")
=> {"sample_first"=>{"Yamada"=>34, "Katou"=>28}, "second_first"=>{"Endou"=>18, "Matsumoto"=>25}}

配列と同様Hashクラスを拡張します

class Hash
  def half (first_key, second_key)
    first_half   = Hash[self.to_a[0..(self.size/2 - 1)]]
    second_half  = Hash[self.to_a[(self.size/2)..self.size]]
    halfed_hash  = {first_key => first_half, second_key => second_half}
  end
end

sample_hash = {"Yamada" => 34, "Katou" => 28, "Endou" => 18, "Matsumoto" => 25}
sample_hash.half("first_half", "second_half")
=> {"first_half"=>{"Yamada"=>34, "Katou"=>28}, "second_half"=>{"Endou"=>18, "Matsumoto"=>25}}

配列とハッシュの処理をまとめる

で最後に配列とハッシュの処理がかなり重複しているのでDRYな感じにしてしましましょう。

class Array
  def half
    first_half   = self.slice(0...self.size/2)
    second_half  = self.slice((self.size/2)..self.size)
    halfed_array = [first_half, second_half]
  end
end

class Hash
  def half (first_key, second_key)
    first_half  = Hash[self.to_a.half[0]] #ここでArrayのhalfを使う
    second_half  = Hash[self.to_a.half[1]] #ここも
    halfed_hash  = {first_key => first_half, second_key => second_half}
  end
end

sample = [1,2,3,4]
sample.half => [[1, 2], [3, 4]]

sample_hash = {"Yamada" => 34, "Katou" => 28, "Endou" => 18, "Matsumoto" => 25}
sample_hash.half("first_half", "second_half")
=> {"first_half"=>{"Yamada"=>34, "Katou"=>28}, "second_half"=>{"Endou"=>18, "Matsumoto"=>25}}

追記

Railsではin_groups_ofというメソッドがあるので、それを使うのが良いです。

sample_even = [1, 2, 3, 4]
sample.in_groups_of(sample.size/2) 
=> [[1, 2], [3, 4]]
sample_odd  =  [1, 2, 3, 4, 5, 6, 7]

# 奇数の場合はオプションにfalseをつけないと足りない分の要素1つがnilで埋まります。
sample.in_groups_of(sample.size/2 + 1, false)
=> [[1, 2, 3, 4], [5, 6, 7]]