5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Ruby】配列の要素を交互に取得する。

Last updated at Posted at 2021-01-16

目的

配列の要素を交互に取得し、二つの配列を作成します。
色々なやり方があるとは思いますが、私はこのような方法をとりました。

別解をいただきました。

方法

array = ['い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る', 'を']

array_A = []
array_B = []

array.each_with_index { |kana, index| index.even? ? array_A << kana : array_B << kana }

p array_A
# => ["い", "は", "ほ", "と", "り", "る"]
p array_B
# => ["ろ", "に", "へ", "ち", "ぬ", "を"]

使用したメソッド

メソッド 意味 マニュアル
each_with_index 各要素とそれに付随する添字(インデックス)に対し処理を繰り返す Enumerable#each_with_index
even? 要素が偶数であればtrueを返す Integer#even?
? : 条件演算子(三項演算子) if・else文を1行で書いたもの 演算子式
<< 配列に要素を追加する Array#<<

解説

まず、空の配列を準備します。

array_A = []
array_B = []

この中に元の配列arrayから交互に要素を入れていきます。
そのために使ったメソッドがeach_with_indexです。

each_with_index

このメソッドは繰り返しを行うeachメソッドに変数として各要素のインデックスを使用できるようにしたものです。

['い', 'ろ', 'は'].each_with_index { |kana, index| p "#{kana} - #{index}" }
# => "い - 0"
#    "ろ - 1"
#    "は - 2"

配列arrayの各要素とインデックスは,

0 1 2 3 4 5 6 7 8 9 10 11

となっているため、これを利用して、インデックスが偶数の時にはarray_Aに、奇数の時にはarray_Bに要素を入れていきます。

条件分岐には条件演算子を使用しました。

? :  (条件演算子)

?の前の条件で分岐し、:の前後で、trueの場合とfalseの場合それぞれの処理を分けています。

num = 1

num == 1 ? "これは 1 です" : "これは 1 ではありません"
# => "これは 1 です"

これは、

num = 1

if num == 1
  "これは 1 です"
else
  "これは 1 ではありません"
end
# => "これは 1 です"

と同じ意味になります。

そして、条件の偶数であるかどうかを確認するため、even?メソッドを使用しました。

even?

このメソッドはレシーバ(.の左側)が偶数であればtrueを、奇数であればfalseを返してくれます。

2.even?
# => true

1.even?
# => false

# 0も偶数です
0.even?
# => true

ちなみに、演算子を使用した計算でも同じことができます。

num = 2

num % 2 == 0
# => true

ある数を2で割った時、余りが0であれば偶数です。

even?でインデックスが偶数か奇数かを確認し、? :で条件分岐したあとは、
<<で配列に要素を足しています。

<< (追加)

<<は配列の末端に要素を一つ追加します。

num_array = [1, 2]

num_array << 3

p num_array
# => [1, 2, 3]


@scivola 様よりご教示いただいた別解

※ 配列が偶数の場合のみです。

array = ['い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る', 'を']

array_A, array_B = array.each_slice(2).to_a.transpose

p array_A
# => ["い", "は", "ほ", "と", "り", "る"]
p array_B
# => ["ろ", "に", "へ", "ち", "ぬ", "を"]

使用したメソッド

メソッド 意味 マニュアル
each_slice 要素を指定の数づつ分ける Enumerable#each_slice
to_a 配列を作る Enumerable#entries
transpose 配列を行列に見立て、行と列を入れ替える Array#transpose

解説

今回はメソッドで配列が作られるため、空の配列を作る必要はありません。

まず、array_A, array_B =
これは多重代入(マニュアル)を使用しています。

num_A, num_B = 1, 2

p num_A
# => 1

p num_B
# => 2

このように、左辺と右辺を2つずつ用意することで、同時に代入が可能になります。
この2つの左辺に代入できる、2つの配列を作っていきます。

まずeach_slice(2)を使用し、配列を2つずつの塊に分けています。

each_slice

このメソッドは複数の要素(配列や範囲オブジェクト)を、()内で指定した数ずつブロック( { }内 )に渡します。

[1, 2, 3, 4, 5, 6].each_slice(2) { |n| p n }
# [1, 2]
# [3, 4]
# [5, 6]

元の配列を二つずつに分けたら、次はそれを配列にしなければなりません。そのために使用するメソッドがto_aです。

to_a

このメソッドは、渡された要素を配列に変換します。

(1..10).to_a
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

上記は連続する(..)、1〜10までの数字を配列に変換しています。

each_sliceは、{}をなくすと、Enumeratorクラスのオブジェクトを返します。

[1, 2, 3, 4, 5, 6].each_slice(2)
# => #<Enumerator: [1, 2, 3, 4, 5, 6]:each_slice(2)>

to_aメソッドはEnumeratorクラスのメソッドでもあるため、渡されたオブジェクトに使用し、配列が作成されます。

aa = [1, 2, 3, 4, 5, 6].each_slice(2)

aa.class      # .classメソッドを使うと、レシーバ(.の左側)のクラスを返してくれます。
# => Enumerator

aa.to_a
# => [[1, 2], [3, 4], [5, 6]]

最後に、2つずつに分けた配列の中の配列を右側と左側で分ければ完了です。
つまり、上記の数字を例にすると、
[[ 1, 2 ],[ 3, 4 ],[ 5, 6 ]]
のように赤の数字の配列青の数字の配列に分けることができれば、元の配列を交互に取得して、二つの配列を作成したことになります。

そのためのメソッドがtransposeです。

transpose

このメソッドは配列の中の配列を行列に見立て、行と列を入れ替えます。

[[0, 1], [0, 1], [0, 1]].transpose
# => [[0, 0, 0], [1, 1, 1]]

先程のの数字で例えると、

1 2
3 4
5 6

が、

1 3 5
2 4 6

となるようなイメージです。

これで2つの配列ができたため、多重代入でそれぞれの変数に代入されます。



@jnchito 様よりご教示いただいた別解

array = ['い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る', 'を']

array_A, array_B = array.partition.with_index { |_, index| index.even? }

p array_A
# => ["い", "は", "ほ", "と", "り", "る"]
p array_B
# => ["ろ", "に", "へ", "ち", "ぬ", "を"]

使用したメソッド

メソッド 意味 マニュアル
partition 各要素を条件分岐し、2つの配列を作成する。 Enumerable#partition
with_index 繰り返し処理の変数に、インデックスが使えるようになる。 Enumerator#with_index
even? 要素が偶数であればtrueを返す Integer#even?

解説

今回もメソッドで配列が作られるため、変数に配列の定義は必要ありません。
さらに多重代入で、2つの配列を同時に代入します。

そして、その二つの配列を作るためのメソッドがpartitionです。

partition

このメソッドは、配列の各要素をブロック( { }内 )の条件を満たすか否かで2つの配列に分けます。

[1, 2, 3, 4, 5].partition { |num| (num % 2) == 0 }
# => [[2, 4], [1, 3, 5]]

このメソッドを使用して、偶数の場合とそれ以外(奇数)の場合の配列を作ります。

しかし、partitionメソッドが使用できるブロックの変数は、要素の変数しか使用できません。(上記の場合、numの部分)
今回、偶数・奇数を判断したいのは要素に付随する添字(index)の値です。

そのために必要になるメソッドがwith_indexです。

with_index

このメソッドは、本来indexが変数として使用できない繰り返し処理(mapメソッド等)に、indexを使用できるようにするものです。

['い', 'ろ', 'は'].map.with_index { |kana, index| "#{index}-#{kana}" }
# => ["0-い", "1-ろ", "2-は"]

これを利用して、indexが偶数の場合と奇数の場合をeven?で判断し、2つの配列を作成します。

even? (odd?)

このメソッドは先も出た通り、レシーバ(.の左側)が偶数であればtrueを、奇数であればfalseを返してくれます。

ちなみに、odd?というメソッドもあり、これはeven?の逆になります。
レシーバが奇数であればtrueを、偶数であればfalseを返してくれます。

1.odd?
# => true

2.odd?
# => false

変数 _

今回、partition.with_indexのブロックで使用する変数はindexのみで、要素は使用しません。そのため、要素の変数を_にしています。

array.partition.with_index { |_, index| index.even? }

参考:変数名やブロックパラメーターをアンダースコア1文字にするイディオム - Qiita



最後に

さらに良い方法がございましたら、ご教示いただけますと幸です。
ご覧いただき、ありがとうございました。

5
2
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?