Ruby プログラミング

[Ruby]配列の基本操作2

array_2

こんにちは!じゃいごテックのあつしです。

配列を自由に扱うことが出来ればアルゴリズム問題も解けるようになるかと思います!
「配列の基本操作」後半も張り切っていきましょう!

配列の要素数を数える

length、size メソッド

配列.length

配列の要素数を返し、配列が空の場合は 0 を返します。length、sizeのどちらを使っても同じ結果が得られます。

ary1 = ["a", "b", "c", 1, 2, 3]
p ary1.length
# > 6
ary2 = [[1, 2], [3, 4], [5, 6]]
p ary2.length
# > 3
count メソッド

配列.count(数える値)
配列.count{ |変数| 変数の評価 }

引数を省略すると配列の要素数を返し、引数を与えた場合は引数と一致する要素数を返します。
また、ブロックを指定すると、ブロックを評価して true になった要素数を返します。

ary = [10, 20, 30, 40, 50, 60]
p ary.count
# > 6
p ary.count{ |num| num >= 40}
# > 3
countメソッドを使ってpaiza練習問題を解いてみる
指定の要素のカウント (paizaランク D 相当)

※ paiza レベルアップ問題集 配列活用メニューより

問題:

配列 A の要素数 N と整数 K, 配列 A の各要素 A_1, A_2, ..., A_N が与えられるので、配列 A に K がいくつ含まれるか数えてください。

1 行目に 要素数 N と 整数 K が半角スペースで与えられ、続く N 行で配列 A の要素が与えられる。

入力される値

N K
A_1
...
A_N

期待する出力

配列 A に含まれる K の数を 1 行で出力してください。
また、出力の末尾には改行を入れてください。

条件

  • 1 ≦ N ≦ 100
  • 0 ≦ A_i, K ≦ 100 (1 ≦ i ≦ N)

入力例1
1 2
1
出力例1
0

入力例2
5 4
1
2
3
4
5
出力例2
1

解答例:

# [解答例1]
# 要素数 N と 整数 K を受け取る
N, K = gets.split.map(&:to_i)
# 配列 A を受け取る(A -> ary とする)
ary = N.times.map{ gets.to_i }
# 配列 ary に含まれている整数 K の数を出力
puts ary.count(K)

# [解答例2]
# 入力を一括で受け取る(A -> ary とする)
tmp_line, *ary = STDIN.read.split("\n")
N, K = tmp_line.split.map(&:to_i)
puts ary.map!(&:to_i).count(K)

配列要素の位置を求める

index、find_index メソッド

配列.index(指定値)

配列に指定値を持つ要素があれば、左(先頭)から見て最初のインデックス番号を返します。指定値を持つ要素が存在しなかった場合は nil を返します。
ブロックを渡した場合は評価が true になった要素のインデックスを返します。
find_indexでも同じ結果が得られます。

ary = ["a", "b", "c", "d", "c", "f", "g"]
p ary.index("c")
# > 2
p ary.index("z")
# > nil
indexメソッドを使ってpaiza練習問題を解いてみる
指定要素の先頭位置 (paizaランク D 相当)

※ paiza レベルアップ問題集 配列活用メニューより

問題:

配列 A の要素数 N と整数 K , 配列 A の各要素 A_1, A_2, ..., A_N が与えられるので、K であるような A の要素のうち、要素番号が最小のものを出力してください。
A に K が含まれない場合は -1 を出力してください。

入力される値

N K
A_1
...
A_N

  • 1 行目では、配列 A の要素数 N と整数 K が半角スペース区切りで与えられます。
  • 続く N 行では、配列 A の要素が先頭から順に与えられます。

期待する出力

A に K が含まれる場合は K であるような A の要素のうち要素番号が最小のものを、含まれない場合は -1 を 1 行で出力してください。
また、出力の末尾には改行を入れてください。

条件

  • 1 ≦ N ≦ 100
  • 0 ≦ K , A_i ≦ 100 (1 ≦ i ≦ N)

入力例1
1 0
1
出力例1
-1

入力例2
5 3
1
2
3
4
3
出力例2
3

解答例:

# [解答例1]
# 要素数 N と 整数 K を受け取る
N, K = gets.split.map(&:to_i)
# 配列 A を受け取る(A -> ary とする)
ary = N.times.map{ gets.to_i }

# 配列 A に K が含まれているか調べる
idx = ary.index(K)
# idx が nil 以外なら K の場所(idx+1)、 nil なら -1 を出力
if idx
  puts idx + 1
else
  puts -1
end

# [解答例2]
# 入力を一括で受け取る(A -> ary とする)
tmp_line, *ary = STDIN.read.split("\n")
N, K = tmp_line.split.map(&:to_i)

# 配列 A に K が含まれているか調べる
idx = ary.map!(&:to_i).index(K)

# idx が nil 以外なら K の場所(idx+1)、 nil なら -1 を出力
puts idx ? idx + 1 : -1
rindex メソッド

配列.rindex(指定値)

配列に指定値を持つ要素があれば、左(先頭)から見て最後のインデックス番号を返します。指定値を持つ要素が存在しなかった場合は nil を返します。

ary = ["a", "b", "c", "d", "c", "f", "g"]
p ary.rindex("c")
# > 4
p ary.rindex("z")
# > nil

配列の任意の位置に要素を挿入する

insertメソッド

配列.insert(インデックス, 要素)

インデックスで指定した位置に第2引数以降の値を挿入し、配列を上書きします。

ary = ["a", "b", "c", "d", "e"]
ary.insert(1, 2)
p ary
# > ["a", 2, "b", "c", "d", "e"]
ary.insert(3, 4, 5)
# > ["a", 2, "b", 4, 5, "c", "d", "e"]
insertメソッドを使ってpaiza練習問題を解いてみる
指定位置への要素の追加(paizaランク D 相当)

※ paiza レベルアップ問題集 配列活用メニューより

問題:

配列 A と追加する位置 n と追加する要素 B が与えられるので、B を A_n の後ろに追加した後の A を出力してください。

入力される値

N
A_1
...
A_N
n B

  • 1 行目では、配列 A の要素数 N が与えられます。
  • 続く N 行では、配列 A の要素が先頭(A_1)から順に与えられます。
  • 最後の行では、数値を追加する要素番号 n と、追加する値 B が与えられます。

期待する出力

A_1
...
A_(N+1)

配列 A に B を追加した後の A の全ての要素を以上の形式で出力してください。
また、出力の末尾には改行を入れてください。

条件

  • 1 ≦ N ≦ 100
  • 0 ≦ A_i , B ≦ 100 (1 ≦ i ≦ N)
  • 1 ≦ n ≦ N

入力例1
1
1
1 2
出力例1
1
2

入力例2
5
1
2
3
4
5
3 10
出力例2
1
2
3
10
4
5

解答例:

# [解答例1]
# 要素数 N を受け取る
N = gets.to_i
# 配列 A を受け取る(A -> ary とする)
ary = N.times.map{ gets.to_i }
# 挿入位置 n と 値 B を受け取る
n, B = gets.split.map(&:to_i)

# 配列 A の インデックス n に B を挿入
ary.insert(n, B)
# 配列 A の要素を出力
puts ary

# [解答例2]
# 入力を一括で受け取る(N は不要、A -> ary とする)
_, *ary, tmp_line = STDIN.read.split("\n")
# 要素を整数型に変換
ary.map!(&:to_i)
# 挿入位置 n と 値 B を受け取る
n, B = tmp_line.split.map(&:to_i)

# 配列 A の インデックス n に B を挿入し、要素を出力
puts ary.insert(n, B)

配列先頭への要素挿入・取り出し

shift メソッド

shift(取り出す要素数)

配列の先頭の要素を取り除いてそれを返します。引数が省略された場合は要素を1つ返し、引数が与えられた場合はその個数分の配列を返します。

ary = [1, 2, 3, 4, 5]
p ary.shift
# > 1
p ary
# > [2, 3, 4, 5]
p ary.shift(2)
# > [2, 3]
p ary
# > [4, 5]
unshift メソッド

unshift(追加する要素)

引数に与えた要素を引数の末尾から順番に配列の先頭に挿入します。

ary = [1, 2, 3]
ary.unshift(0)
p ary
# > [0, 1, 2, 3]
ary.unshift([4, 5])
p ary
# > [[4, 5], 0, 1, 2, 3]
ary.unshift(*[6, 7])
p ary
# > [6, 7, [4, 5], 0, 1, 2, 3]
ary.unshift(8, 9)
p ary
# > [8, 9, 6, 7, [4, 5], 0, 1, 2, 3]

配列末尾への要素挿入・取り出し

pop メソッド

pop(取り出す要素数)

配列の末尾から要素を取り除いてそれを返します。引数が省略された場合は要素を1つ返し、引数が与えられた場合はその個数分の配列を返します。

ary = [1, 2, 3, 4, [5, 6], 7]
p ary.pop
# > 7
p ary
# > [1, 2, 3, 4, [5, 6]]
p ary.pop
# > [5, 6]
p ary
# > [1, 2, 3, 4]
p ary.pop(2)
# > [3, 4]
p ary
# > [1, 2]
push メソッド

push(追加する要素)

引数に与えた要素を配列の末尾に挿入します。

ary = [1, 2, 3]
ary.push(4)
p ary
# > [1, 2, 3, 4]
ary.push([5, 6])
p ary
# > [1, 2, 3, 4, [5, 6]]
ary.push(*[7, 8])
p ary
# > [1, 2, 3, 4, [5, 6], 7, 8]
ary.push(9, 10)
p ary
# > [1, 2, 3, 4, [5, 6], 7, 8, 9, 10]

要素の最小値・最大値を求める

minメソッド

配列.min

配列内の最小値を返します。配列が空の場合は nil を返します。

ary = [3, 5, 2, 1, 6, 4]
p ary.min
# > 1
maxメソッド

配列.max

配列内の最大値を返します。配列が空の場合は nil を返します。

ary = [3, 5, 2, 1, 6, 4]
p ary.max
# > 6
minmaxメソッド

配列.minmax

配列内の最小値と最大値を [最小値, 最大値] の配列で返します。配列が空の場合は [nil, nil] を返します。

ary = [3, 5, 2, 1, 6, 4]
p ary.minmax
# > [1, 6]
minmaxメソッドを使ってpaiza練習問題を解いてみる
配列の最大最小 (paizaランク D 相当)

※ paiza レベルアップ問題集 配列メニューより

問題:

1 行目に整数 N が与えられます。
2 行目に N 個の整数 a_1, a_2, ..., a_N が与えられます。
N 個の整数のうち、最大の数と最小の数を半角スペース区切りで出力してください。
N 個の整数を大きい順や小さい順に並び替える操作を考えて解いてみましょう。

入力される値

1 行目に整数 N が与えられます。
2 行目に N 個の整数 a_1, a_2, ..., a_N が与えられます。
以下の形式で標準入力によって与えられます。

N
a_1 a_2 ... a_N

期待する出力

N 個の整数のうち、最大の数と最小の数を半角スペース区切りで出力してください。
また、末尾に改行を入れ、余計な文字、空行を含んではいけません。

a_MAX a_MIN

条件

すべてのテストケースにおいて、以下の条件をみたします。

  • N は 1 以上 10 以下の整数
  • a_i (1 ≤ i ≤ N) は 1 以上 100 以下の整数

解答例:

# [解答例1]
# 要素数 N を受け取る(使わない)
_ = gets.to_i
# 配列 A を受け取る(A -> ary とする)
ary = gets.split.map(&:to_i)
# 最小値・最大値を取得する
min_num, max_num = ary.minmax

# 最大値、最小値の順で半角スペース区切りで表示
puts [max_num, min_num].join(" ")
# 下記でもOK
# puts "#{max_num} #{min_num}"

# [解答例2]
# 入力を一括で受け取る(N は不要)
_, tmp_line = STDIN.read.split("\n")
# 配列 A を生成して要素を整数型に変換(A -> ary とする)
ary = tmp_line.split.map(&:to_i)
# 最大値、最小値の順で半角スペース区切りで表示 
puts ary.minmax.reverse.join(" ")

全要素の合計を求める

sumメソッド

配列.sum

全要素の合計を返します。空配列の場合 0 を返します。

p [1, 2, 3, 4, 5].sum
# > 15
p [].sum
# > 0
sumメソッドを使ってpaiza練習問題を解いてみる
全ての要素の和 (paizaランク D 相当)

※ paiza レベルアップ問題集 配列活用メニューより

問題:

配列 A の要素数 N と配列 A の各要素 A_1, A_2, ..., A_N が整数として与えられるので、配列 A の全ての要素の和を求めてください。

入力される値

N
A_1
...
A_N

  • 1 行目では、配列 A の要素数 N が与えられます。
  • 続く N 行では、配列 A の要素が先頭から順に与えられます。

期待する出力

配列 A の全ての要素の和を 1 行で出力してください。
また、出力の末尾には改行を入れてください。

条件

  • 1 ≦ N ≦ 100
  • 0 ≦ A_i ≦ 100 (1 ≦ i ≦ N)

入力例1
1
1
出力例1
1

入力例2
5
1
2
3
4
5
出力例2
15

解答例:

# [解答例1]
# 要素数 N を受け取る
N = gets.to_i
# 配列 A を受け取る(A -> ary とする)
ary = N.times.map { gets.to_i }

# 全ての要素の和を出力する
puts ary.sum

# [解答例2]
# 入力を一括で受け取る(N は不要、 A -> ary とする)
_, *ary = STDIN.read.split("\n").map(&:to_i)

# 全ての要素の和を出力する
puts ary.sum

配列の順番を並び替える

reverse、reverse!メソッド

配列.reverse

reverseは配列の要素を逆にした配列を返します。reverse!は配列の要素を逆にして元の配列を上書きします。

ary = [1, 2, 3, 4, 5]
p ary.reverse
# > [5, 4, 3, 2, 1]
p ary
# > [1, 2, 3, 4, 5]

ary.reverse!
p ary
# > [5, 4, 3, 2, 1]
reverseメソッドを使ってpaiza練習問題を解いてみる
配列の反転 (paizaランク D 相当)

※ paiza レベルアップ問題集 配列メニューより

問題:

1 行目に整数 N が与えられます。
2 行目に N 個の整数 a_1, a_2, ..., a_N が与えられます。
N 個の整数の順番を反転させ、改行区切りで出力してください。

入力される値

1 行目に整数 N が与えられます。
2 行目に N 個の整数 a_1, a_2, ..., a_N が与えられます。
以下の形式で標準入力によって与えられます。

N
a_1 a_2 ... a_N

期待する出力

N 個の整数の順番を反転させ、改行区切りで出力してください。
また、末尾に改行を入れ、余計な文字、空行を含んではいけません。

a_N
a_(N-1)
...
a_1

条件

  • N は 1 以上 10 以下の整数
  • a_i (1 ≦ i ≦ N) は 1 以上 10 以下の整数
入力例1
5
1 5 2 4 3
出力例1
3
4
2
5
1

解答例:

# [解答例1]
# 要素数 N を受け取る(使わない)
_ = gets.to_i
# 配列 A を受け取る(A -> ary とする)
ary = gets.split.map(&:to_i)

# 要素の並びを逆にして、先頭から順に出力する
puts ary.reverse

# [解答例2]
# 入力を一括で受け取る(N は不要)
_, tmp_line = STDIN.read.split("\n")
# 配列 A を受け取る(A -> ary とする)
ary = tmp_line.split.map(&:to_i)

# 要素の並びを逆にして、先頭から順に出力する
puts ary.reverse
sort、sort!メソッド

配列.sort

sortは配列の要素を昇順に並び替えた配列を返します。sort!は配列の要素を昇順に並び替えて元の配列を上書きします。
数値は小から大の順、文字列は辞書順に並び替えます。

# 数値の場合
ary = [52, 9, 19, 44]
p ary.sort
# > [9, 19, 44, 52]
ary.sort!
p ary
# > [9, 19, 44, 52]

# 文字列の場合
ary = ["52", "9", "19", "44"]
p ary.sort
# > ["19", "44", "52", "9"]
ary.sort!
p ary
# > ["19", "44", "52", "9"]
sortメソッドを使ってpaiza練習問題を解いてみる
配列のソート (paizaランク D 相当)

※ paiza レベルアップ問題集 配列メニューより

問題:

1 行目に整数 N が与えられます。
2 行目に N 個の整数 a_1, a_2, ..., a_N が与えられます。
N 個の整数を小さい順にソートし、改行区切りで出力してください。

入力される値

1 行目に整数 N が与えられます。
2 行目に N 個の整数 a_1, a_2, ..., a_N が与えられます。
以下の形式で標準入力によって与えられます。

N
a_1 a_2 ... a_N

期待する出力

N 個の整数を小さい順にソートし、改行区切りで出力してください。
また、末尾に改行を入れ、余計な文字、空行を含んではいけません。

条件

  • N は 1 以上 10 以下の整数
  • a_i (1 ≦ i ≦ N) は 1 以上 10 以下の整数

入力例1
5
5 4 3 2 1
出力例1
1
2
3
4
5

解答例:

# [解答例1]
# 要素数 N を受け取る(使わない)
_ = gets.to_i
# 配列 A を受け取る(A -> ary とする)
ary = gets.split.map(&:to_i)

# 要素の並びを逆にして、先頭から順に出力する
puts ary.sort

# [解答例2]
# 入力を一括で受け取る(N は不要)
_, tmp_line = STDIN.read.split("\n")
# 配列 A を受け取る(A -> ary とする)
ary = tmp_line.split.map(&:to_i)

# 要素の並びを逆にして、先頭から順に出力する
puts ary.sort
sort_by、sort_by!メソッド

配列.sort_by{ |変数| 評価式 }

sort_byはブロックの評価結果で昇順に並び替えた配列を返します。sort_by!はブロックの評価結果で昇順に並び替えて元の配列を上書きします。

# 文字列を整数化してソートする
ary = ["52", "9", "19", "44"]
p ary.sort_by{ |str_num| str_num.to_i }
# > ["9", "19", "44", "52"]
ary.sort_by!{ |str_num| str_num.to_i }
p ary
# > ["9", "19", "44", "52"]

全ての要素を評価する

all?メソッド

配列.all?{ |変数| 評価式 }

各要素を評価し、全て true の場合true を返します。

# 配列の要素が全て3の倍数ならtrue
ary1 = [3, 6, 9, 12, 15]
p ary1.all?{ |num| num % 3 == 0 }
# > true
ary2 = [3, 6, 11, 12, 15]
p ary2.all?{ |num| num % 3 == 0 }
# > false
any?メソッド

配列.all?{ |変数| 評価式 }

各要素を評価し、一つでも true の場合true を返します。

# 配列の要素で一つでも70以上があればtrue
ary1 = [30, 69, 67, 70, 50]
p ary1.any?{ |num| num >= 70 }
# > true
ary2 = [30, 69, 67, 68, 50]
p ary2.any?{ |num| num >= 70 }
# > false

配列を転置する

transposeメソッド

二次元配列.transpose

二次元配列の行と列を入れ替えた配列を返します。転置できない場合はエラーが発生します。

ary1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
p ary1.transpose
# > [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

# 転置できない場合
ary2 = [1, 2, 3]
p ary2.transpose
# > TypeError (no implicit conversion of Integer into Array)

ary3 = [[1, 2, 3], [4], [5, 6, 7]]
p ary3.transpose
# > IndexError (element size differs (1 should be 3))
transposeメソッドを使ってpaiza練習問題を解いてみる
五目並べ(縦) (paizaランク C 相当)

※ paiza レベルアップ問題集 配列メニューより

問題:

5行5列の五目並べの盤面が与えられます。
盤面の各マスには、"O"か"X"か"."が書かれています。
"O"と"X"は、それぞれプレイヤーの記号を表します。
同じ記号が縦に連続で5つ並んでいれば、その記号のプレイヤーが勝者となります。

勝者の記号を1行で表示してください。
勝者がいない場合は、引き分けとして、"D"を表示してください。

入出力例:

入力される値

入力は以下のフォーマットで与えられます。
s_1
s_2
s_3
s_4
s_5

期待する出力

勝者の記号を1行で表示してください。
勝者がいない場合は、引き分けとして、"D"を表示してください。

条件

すべてのテストケースにおいて、以下の条件をみたします。

  • s_iの文字数は5文字
  • s_iに含まれる文字は"O"か"X"か"."のいずれか
  • 勝者が2人になる盤面が、与えられることはありません。

入力例1
XXOXO
OXOXX
OOOOO
OXOX.
XOXXO
出力例1
D

入力例2
XXOXO
OXOXX
.OXXO
OXOO.
XXXXX
出力例2
D

入力例3
...X.
...X.
...X.
...X.
OOOO.
出力例3
D

解答例:

# [解答例1]
# 5行5列なので H = 5, W = 5
H, W = 5, 5
# 文字列を受け取り1文字で分割して5 x 5 の二次元配列 board を作る
board = H.times.map{ gets.chomp.split("") }
# これでもOK
# board = H.times.map{ gets.chomp.chars }

# board の行列を入れ替えた transposed_board を作成
transposed_board = board.transpose

# 勝者を "D": Draw で初期化
winner = "D"
# 縦のラインを順番に確認する 
transposed_board.each do |line|
  # "O" の勝ち
  if line.count("O") == 5
    winner = "O"
    break
  # "X" の勝ち
  elsif line.count("X") == 5
    winner = "X"
    break
  end
end

# winner を出力
puts winner

# [解答例2]
# 5行5列なので H = 5, W = 5
H, W = 5, 5

# 文字列を受け取り1文字で分割して5 x 5 の二次元配列 board を作る
board = H.times.map{ gets.chomp.chars }

# board の行列を入れ替えた transposed_board を作成
transposed_board = board.transpose

# 勝者を "D": Draw で初期化
winner = "D"
# 縦のラインを順番に確認する
transposed_board.each do |line|
  # "O", "X", "." の数を集計する
  summary = line.group_by(&:itself)

  # 集計結果の要素が一つ(マークが全て揃ってる)で、かつそのキーが "." 以外なら決着
  if summary.length == 1 && summary.keys[0] != "."
    # winner を上書きして繰り返し処理を抜ける
    winner = summary.keys[0]
    break
  end
end

# winner を出力
puts winner

今回のまとめ

2回に分けて配列の基本操作を一通り紹介し、最後はちょっぴりアルゴリズム要素が入った練習問題を解いてみました。
配列操作メソッドは他にもたくさんありますので、Rubyリファレンスマニュアルを時々覗いて精進したいと思います!

だいぶ長くなってしまいましたが、配列の基本操作はこれで一旦完了です。お疲れ様でした!
抜けなどがあれば追加や修正していきたいと思います!

【PR】Ruby学習でお世話になった本




【PR】アルゴリズム学習でお世話になった本


アルゴリズム関連の技術書は大抵C/C++で書かれていますが、ある程度プログラムが出来れば、処理の流れは理解することが出来ます。






通称: 螺旋本。C++で解説されています。AOJ(Aizu Online Judge)を運営している会津大学の渡辺准教授が書いた本です。データ構造や計算量の内容から丁寧に書いてありますのでアルゴリズム学習の最初の参考書としてオススメです。







通称: 蟻本。C++で解説されています。競技プログラミング中級者の定番書と言われていて、競技プログラミングで利用できる色々なアルゴリズムを学ぶことが出来ます。かなり高度な内容も含まれているので1冊分を完全に理解するのにはかなりの時間がかかりそうですが、手元に置いて何度も読み返しています。







通称: チーター本。C#, C++, JAVAでTopcoderの問題を解説しています。初心者~中級者向けに書かれているので解説が非常に丁寧です。







Python3でアルゴリズムを解説している本です。講義スタイルの本で、図やフローチャートを使ってアルゴリズムとデータ構造についてしっかりと説明されています。代わりにコードは少なめです。Pythonコードが読めなくても十分理解できると思います。


-Ruby, プログラミング
-,

© 2024 じゃいごテック