こんにちは!じゃいごテックのあつしです。
paizaスキルチェックやスキルアップ問題集でよく使う配列の操作についてご紹介します!
配列はとてもボリュームが多いので数回に分けてご紹介しようと思います。
配列とは
複数の様々な型の要素の集まりを一列に並べてリストにしたデータ構造のことです。
配列の中に配列を格納することで多次元のデータも扱うことが出来ます。
アルゴリズムを理解する上で非常に重要なデータ構造のひとつです。
配列を生成する
[] 角カッコを使った生成方法
[] の中に , カンマ区切りの要素を入れると配列オブジェクトを生成することが出来ます。おそらく一番多く使うパターンです。
# 空の配列 [] # 整数の配列 [123, 456, 789] # 文字列の配列 ["abc", "def", "ghi"] # 2次元の配列 [[10, 20, 30], [40, 50, 60], [70, 80, 90]] # 色々な型が混在している配列 ["abc", "def", 10, 20, ["hij", 30, 40]]
Array.new を使った生成方法
Array.new(要素数, 初期値)
初期値を揃えて配列を生成したい場合に使用します。
# 要素数 5、初期値 nil で初期化 p Array.new(5) # > [nil, nil, nil, nil, nil] # 要素数 6、初期値 0 で初期化 p Array.new(6, 0) # > [0, 0, 0, 0, 0, 0] # 二次元配列 # 行数 4、 要素数 6、 初期値 0 で初期化 p Array.new(4){ Array.new(6, 0) } # > [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]
to_a メソッドを使った生成方法
範囲オブジェクト.to_a
範囲オブジェクトから配列を生成することが出来ます。
# 0から9までの連番の配列を生成 p (0..9).to_a # > [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
配列を結合する
+ 演算子、concat メソッド
+
演算子、concat
メソッドで配列同士を結合することが出来ます。
concat
メソッドは元の配列を上書きします。
ary1 = [1, 2, 3] ary2 = ["a", "b", "c"] p ary1 + ary2 # > [1, 2, 3, "a", "b", "c"] p ary1 # > [1, 2, 3] p ary1.concat(ary2) # > [1, 2, 3, "a", "b", "c"] p ary1 # > [1, 2, 3, "a", "b", "c"]
| 演算子、uniq メソッド
|
演算子、uniq
メソッドで重複を取り除くことが出来ます。
ary3 = ["a", "b", "c", "d", "e"] ary4 = ["c", "d", "e", "f", "g"] p ary3 | ary4 # > ["a", "b", "c", "d", "e", "f", "g"] p (ary3 + ary4).uniq # > ["a", "b", "c", "d", "e", "f", "g"]
uniqメソッドを使ってpaiza練習問題を解いてみる
重複要素の削除 (paizaランク D 相当)
※ paiza レベルアップ問題集 配列活用メニューより
問題:
配列 A の要素数 N と配列 A の各要素 A_1, A_2, ..., A_N が与えられるので、同じ値の要素が 2 つ以上含まれないように A を加工した新たな配列 B を作成してください。
ただし、 A に同じ値の要素が 2 つ以上あった場合、それらのうち先頭の要素を B に採用するものとします。
入力される値
N
A_1
...
A_N
- 1 行目では、配列 A の要素数 N が与えられます。
- 続く N 行では、配列 A の要素が先頭から順に与えられます。
期待する出力
B_1
...
...
同じ値の要素が 2 つ以上含まれないように A を加工した新たな配列 B の各要素を改行区切りで出力してください。
また、出力の末尾には改行を入れてください。
条件
- 1 ≦ N ≦ 100
- 0 ≦ A_i ≦ 100 (1 ≦ i ≦ N)
入力例1
1
1
出力例1
1
入力例2
5
1
2
3
5
2
出力例2
1
2
3
5
解答例:
# [解答例1] # 要素数 N を受け取る N = gets.to_i # 配列 A を受け取る A = N.times.map{ gets.to_i } # A の重複要素を削除した配列Bを作成 B = A.uniq # Bの要素を出力する B.each{ |element| puts element } # [解答例2] # 入力を一括で受け取る(最初の入力 N は使わない) _, *A = STDIN.read.split("\n").map(&:to_i) # A の重複要素を削除した配列Bを作成 B = A.uniq # Bの要素を出力する puts B
要素の取得と書き換え
要素の取得
インデックス(添え字)を指定することにより要素の取得や書き換えを行うことが出来ます。
["apple", "banana", "cherry", 123, 456]
という配列があった場合、インデックスは下記表のようになります。
配列の要素 | "apple" | "banana" | "cherry" | 123 | 456 |
前からのインデックス | 0 | 1 | 2 | 3 | 4 |
後ろからのインデックス | -5 | -4 | -3 | -2 | -1 |
ary = ["apple", "banana", "cherry", 123, 456] # 3番目の要素を取得する p ary[2] # > "cherry" # 2番目から4番目の要素を取得する p ary[1..3] # > ["banana", "cherry", 123] # 後ろから2番目の要素を取得する p ary[-2] # > [123] # 最初から3番目までの要素を取得する p ary[..2] # > ["apple", "banana", "cherry"] # 3番目から最後までの要素を取得する p ary[2..] # > ["cherry", 123, 456]
要素の取得を使ってpaiza練習問題を解いてみる
ランダムアクセス (paizaランク D 相当)
※ paiza レベルアップ問題集 データセット選択メニューより
問題:
要素数Nの数列Aと数値Mが与えられます。AのM番目の値を出力してください。
入力される値
N M
A
条件
- N, M は 1 以上 100 以下
- 1 ≦ M ≦ N
- A の各要素の値は 1 以上 100 以下
入力例1
5 2
1 2 3 4 5
出力例1
2
入力例2
3 3
7 8 9
出力例2
9
解答例:
# [解答例] # 要素数 N と数値 M を受け取る N, M = gets.split.map(&:to_i) # 数列 A を受け取る A = gets.split.map(&:to_i) # 数列 A の M 番目の要素を出力する # インデックスは 0 スタートなので M 番目のインデックスは M-1 puts A[M-1]
要素の書き換え
インデックスを指定して新たな値を代入すると、要素を上書きすることが出来ます。
herbs = ["basil", "oregano", "thyme", "elder"] # 4番目を上書きする herbs[3] = "laurel" p herbs # > ["basil", "oregano", "thyme", "laurel"] # 2番目から4番目を上書きする herbs[2..4] = "rosemary" p herbs # > ["basil", "rosemary"]
要素の書き換えを使ってpaiza練習問題を解いてみる
指定要素の入れ替え (paizaランク D 相当)
※ paiza レベルアップ問題集 データセット選択メニューより
問題:
配列 A の要素数 N と配列 A の各要素 A_1, A_2, ..., A_N , 交換する A の要素番号 X, Y が与えられるので、A_X と A_Y を入れ替えた後の A を出力してください。
入力される値
N
A_1
...
A_N
X Y
- 1 行目では、配列 A の要素数 N が与えられます。
- 続く N 行では、配列 A の要素が先頭の A_1 から順に与えられます。
- 最後の 1 行で、X, Y が半角スペース区切りで与えられます。
期待する出力
A_1
...
A_N
A_X と A_Y を入れ替えた後の A の各要素を以上の形式で出力してください。
条件
- 2 ≦ N ≦ 100
- 1 ≦ X , Y ≦ N , X != Y
- 0 ≦ A_i ≦ 100 (1 ≦ i ≦ N)
入力例1
2
1
10
1 2
出力例1
10
1
入力例2
5
1
2
3
4
5
3 5
出力例2
1
2
5
4
3
解答例:
# [解答例1] # 要素数 N を受け取る N = gets.to_i # 配列 A を受け取る(A -> ary とする) ary = N.times.map{gets.to_i} # 番号 X, Y を 受け取る(インデックスは0スタートなので 番号-1 ) X, Y = gets.split.map{ |str_num| str_num.to_i - 1} # ary[X] と ary[Y] を入れ替える ary[X], ary[Y] = ary[Y], ary[X] # ary 要素を1行ごとに出力 ary.each{ |element| puts element} # [解答例2] # 一括で受け取る(最初の入力 N は使わない) _, *ary, tmp_line = STDIN.read.split("\n") # ary を Integer に変換 ary.map!(&:to_i) # tmp_line から X,Y を取り出す(インデックスは0スタートなので 番号-1 ) X, Y = tmp_line.split.map{ |str_num| str_num.to_i - 1 } # ary[X] と ary[Y] を入れ替える ary[X], ary[Y] = ary[Y], ary[X] # ary の要素を1行ごとに出力する puts ary
配列の要素を削除する
delete メソッド
配列.delete(削除する値)
引数に指定した値を全て削除します。
# 値が "c" の要素をすべて削除 ary1 = ["a", "b", "c", "d", "c", "e", "b"] ary1.delete("c") p ary1 # > ["a", "b", "d", "e", "b"]
delete_at メソッド
配列.delete_at(インデックス)
引数に指定したインデックスの要素を削除します。
# ary2[2] を削除 ary2 = ["a", "b", "c", "d", "c", "b", "a"] ary2.delete_at(2) p ary2 # > ["a", "b", "d", "c", "b", "a"]
delete_if メソッド
配列.delete_if{ |変数| 変数の評価 }
要素を順番に評価して、true になった要素をすべて削除します。
# 3の倍数を削除する ary3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] ary3.delete_if{ |num| num % 3 == 0 } p ary3 # > [1, 2, 4, 5, 7, 8, 10, 11]
delete_atメソッドを使ってpaiza練習問題を解いてみる
指定要素の削除 (paizaランク D 相当)
※ paiza レベルアップ問題集 配列活用メニューより
問題:
配列 A とその要素数 N と削除する要素の番号 n が与えられるので、A から A_n を削除し、A を要素数 N - 1 の配列とした後の A の各要素を出力してください。
入力される値
N
A_1
...
A_N
n
1 行目では、配列 A の要素数 N が与えられます。
続く N 行では、配列 A の要素が先頭の A_1 から順に与えられます。
最後の行では、数値を削除する要素番号 n が与えられます。
期待する出力
A_1
...
A_{N-1}
配列 A から指定の要素を削除した後の A の全ての要素を以上の形式で出力してください。
条件
- 1 ≦ N ≦ 100
- 0 ≦ A_i ≦ 100 (1 ≦ i ≦ N)
- 1 ≦ n ≦ N
入力例1
2
1
2
1
出力例1
2
入力例2
5
1
2
3
4
5
3
出力例2
1
2
4
5
解答例:
# [解答例1] # 要素数 N を受け取る N = gets.to_i # 配列 A を受け取る(A -> ary とする) ary = N.times.map{ gets.to_i } # 要素番号 N を 受け取る(N -> idx とする ) # インデックスは 0 スタートなので 番号-1 する idx = gets.to_i - 1 # ary[idx] を削除する ary.delete_at(idx) # ary 要素を1行ごとに出力 puts ary # [解答例2] # 一括で受け取る(最初の入力 N は使わない) _, *ary, idx = STDIN.read.split("\n").map(&:to_i) # インデックスは 0 スタートなので 番号-1 する idx -= 1 # ary[idx] を削除する ary.delete_at(idx) # ary の要素を1行ごとに出力する puts ary
条件を満たす要素を取り出す
select メソッド
配列.select{ |変数| 変数の評価 }
要素を順番に評価して、true になった要素をすべて含む配列を返します。true になる要素が一つもなかった場合は空配列を返します。
# 4の倍数を取り出す ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] p ary.select{ |num| num % 4 == 0 } # > [4, 8, 12] p ary # > [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
reject メソッド
配列.reject{ |変数| 変数の評価 }
要素を順番に評価して、false になった要素をすべて含む配列を返します。false になる要素が一つもなかった場合は空配列を返します。
# 4の倍数"以外"を取り出す ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] p ary.reject{ |num| num % 4 == 0 } # > [1, 2, 3, 5, 6, 7, 9, 10, 11] p ary # > [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
指定する値を持つ要素が含まれているか
include? メソッド
配列.include?(指定値)
配列に指定値を持つ要素が含まれていれば true を返します。
ary1 = ["ab", "cd", "ef","gh", "ij"] p ary1.include?("cd") # > true p ary1.include?("if") # > false ary2 = [["ab", "cd"], ["ef", "gh"],["ij", "kl"]] p ary2.include?("ab") # > false p ary2.include?(["ef", "gh"]) # > true
include?メソッドを使ってpaiza練習問題を解いてみる
指定要素があるかの判定 (paizaランク D 相当)
※ paiza レベルアップ問題集 配列活用メニューより
問題:
配列 A の要素数 N と整数 K , 配列 A の各要素 A_1, A_2, ..., A_N が与えられるので、A に K が 1 つでも含まれている場合は Yes
を、含まれていない場合は No
を出力してください。
入力される値
N K
A_1
...
A_N
- 1 行目では、配列 A の要素数 N と整数 K が半角スペース区切りで与えられます。
- 続く N 行では、配列 A の要素が先頭から順に与えられます。
期待する出力
A に K が 1 つでも含まれている場合は Yes
を、含まれていない場合は No
を 1 行で出力してください。
条件
- 1 ≦ N ≦ 100
- 0 ≦ K , A_i ≦ 100 (1 ≦ i ≦ N)
入力例1
1 0
1
出力例1
No
入力例2
5 3
1
2
3
4
5
出力例2
Yes
解答例:
# [解答例1] # 要素数 N と 整数 K を受け取る N, K = gets.split.map(&:to_i) # 配列 A を受け取る(A -> ary とする) ary = N.times.map{ gets.to_i } # 配列 A に 整数 K が含まれているか? if ary.include?(K) puts "Yes" else puts "No" # [解答例2] # 一括で受けとる(A -> ary とする) tmp_line, *ary = STDIN.read.split("\n") # tmp_line を 要素数 N と 整数 K に分ける N, K = tmp_line.split.map(&:to_i) # 配列 A の要素を整数型に変換 ary.map!(&:to_i) # 配列 A に 整数 K が含まれているか? puts ary.include?(K) ? "Yes" : "No"
今回のまとめ
- 配列は直接データを入力して生成することも出来るし、要素数や初期値を指定して生成することもできる
- 配列はインデックスで要素の取得や代入が出来る
- 配列は足し算のようにつなげることが出来る( +、concat )
- 配列から重複要素を削除することが出来る( |、uniq )
- 配列に条件を与えて抽出・削除することが出来る( delete、select、reject )
- 配列の要素に指定された値が存在するかどうか確認することが出来る(include?)
配列の基本操作2へ続きます。