LoginSignup
67
62

More than 3 years have passed since last update.

【Swift】配列操作まとめ

Last updated at Posted at 2021-02-22

目次

はじめに
部分置換
部分配列の型
配列のプロパティ
配列のメソッド
シーケンス操作
最後に

はじめに

配列操作のまとめです。主に、部分配列、配列のプロパティ、メソッド、シーケンス操作についてまとめています。
SequenceとCollectionや基本的な配列のアクセス方法、その他配列以外の知識が必要なものは今回は説明していません。

部分置換

var a = ["赤", "青", "緑", "白", "黒"]
//①
a[1...1] = ["橙", "茶", "紫"]
print(a) // ["赤", "橙", "茶", "紫", "緑", "白", "黒"]
//②
a[2...3] = ["黄"]
print(a) // ["赤", "橙", "黄", "緑", "白", "黒"]
//③
a[1...2] = []
print(a) // ["赤", "緑", "白", "黒"]
//④
a[2...] = ["銀"]
print(a) // ["赤", "緑", "銀"]
//⑤
a[...] = ["金"]
print(a) // ["金"]

注意
番号ごとに毎回配列の要素が変わっていることに注意してください。(前番号のコメントを参照)
"青""橙", "茶", "紫"に変更しています。ただし、a[1]ではだめです。
② index番号が2から3である"茶""紫""黄"に変更しています。
③ index番号が1から2である"橙""黄" を消去しています。
④ index番号が2から最後の要素までの"白""黒""銀"に変更しています。
...で配列の全要素を指定し、"金"に変更しています。

部分配列の型

let b = ["北", "東", "南", "西"]
let c = b[2...]
print(type(of: c)) // ArraySlice<String>

部分配列とは配列の添字に範囲を指定したものです。(a[4...5], a[2...], a[...1], a[...]など)
これらの型はArray型ではなく、ArraySlice型になります。
ArraySlice型は必ずしも0からインデックス番号が振られるわけではありません。

print(c.startIndex) // 2
print(c[2]) // 南
print(c[3]) // 西

このように、ArraySlice型は必ずしも0からインデックス番号が始まるとは限りません。
また、以下のようにすることでArraySlice型を0からインデックス番号が始まるArray型にすることができます。

let d = [String](c)
print(d) // ["南", "西"]
print(d.startIndex) // 0

配列のプロパティ

概要 宣言 説明
要素数 count 要素数を整数値で返す
先頭要素 first 最初の要素を返す(なければnilを返す)
末尾要素 last 最後の要素を返す(なければnilを返す)
空配列か isEmpty 空配列であればtrue,要素があればfalseを返す
let e = [Character]("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
print(e) // "A"から"Z"までの一文字ずつの配列
print(e.count) // 26
print(e.first!) // A
print(e.last!) // Z
print(e.isEmpty) // false

配列のメソッド

概要 宣言 説明
検索 firstIndex 引数の値が最初に現れる位置の添字を返す(なければnilを返す)
検索 lastIndex 引数の値が最後に現れる位置の添字を返す(なければnilを返す)
部分列 prefix 指定した個数分の先頭部分の部分配列を返す
部分列 suffix 指定した個数分の末尾部分の部分配列を返す
部分列 dropFirst 指定した個数分の先頭要素を取り除いた残りを部分配列として返す
部分列 dropLast 指定した個数分の末尾要素を取り除いた残りを部分配列として返す
追加 append 配列の末尾に値を付け加える
挿入 insert 指定した位置に値を挿入する
削除 remove 指定した位置の要素を削除する
削除 removeFirst 最初の要素を消去する
削除 removeLast 最後の要素を消去する
削除 removeAll 要素を全て消去し、配列の要素数を0にする
整列 sort 要素を昇順に並べ替える
整列 sorted 要素を昇順に並べ替えて新しい配列を返す
ランダム randomElement 要素をランダムに選んで一つ返す
シャッフル shuffle 要素をランダムに並び替える
シャッフル shuffled 要素をランダムに並び替えた配列を返す
逆順 reverse 要素を元の配列と逆にする
逆順 reversed 要素を元の配列と逆にした配列を返す
var f = ["A", "B", "B", "C", "C", "D", "E"]
print(f.firstIndex(of: "B")!) // 1
print(f.lastIndex(of: "C")!) // 4
print(f.prefix(3)) // ["A", "B", "B"]
print(f.suffix(5)) // ["B", "C", "C", "D", "E"]
print(f.dropFirst()) // ["B", "B", "C", "C", "D", "E"]
print(f.dropFirst(3)) // ["C", "C", "D", "E"]
print(f.dropLast()) // ["A", "B", "B", "C", "C", "D"]
print(f.dropLast(5)) // ["A", "B"]
f.append("G") // f -> ["A", "B", "B", "C", "C", "D", "E", "G"]
f.insert("H", at: 3) // -> ["A", "B", "B", "H", "C", "C", "D", "E", "G"]
f.remove(at: 5) // f -> ["A", "B", "B", "H", "C", "D", "E", "G"]
f.removeFirst() // f -> ["B", "B", "H", "C", "D", "E", "G"]
f.removeLast() // f -> ["B", "B", "H", "C", "D", "E"]
f.removeFirst(2) // f -> ["H", "C", "D", "E"]
f.removeLast(2) // f -> ["H", "C"]
f.removeAll() // f -> []
var g = [2, 4, 1, 6, 5, 3]
g.sort() // g -> [1, 2, 3, 4, 5, 6]
var h = ["a", "c", "b", "e", "d"]
let i = h.sorted()
print(h) // ["a", "c", "b", "e", "d"]
print(i) // ["a", "b", "c", "d", "e"]
var 宝くじ = ["100円", "300円", "1000円", "1万円", "100万円", "ハズレ"]
print(宝くじ.randomElement()!) // たとえば"ハズレ"
var j = [2, 4, 1, 6, 5, 3]
j.shuffle() // j -> たとえば[2, 5, 3, 1, 6, 4]
var k = ["a", "e", "c", "d", "b"]
let l = k.shuffled()
print(k) // ["a", "e", "c", "d", "b"]
print(l) // ["d", "a", "e", "c", "b"]
var m = ["S", "w", "i", "f", "t"]
m.reverse() // ["t", "f", "i", "w", "S"]
print(m)
let n = [1, 2, 3]
let o = n.reversed()
print(o) // ReversedCollection<Array<Int>>(_base: [1, 2, 3]) え?

注意するべきことのみ解説します。
prefix, suffix, dropFirst, dropLastはArraySlice型が返されます。
FirstやLastのついたメソッドに引数を指定するとたとえばLastであれば、最後から引数で指定したインデックス番号までを操作の対象とします。
sort()sorted()の違いはsort()が配列そのものを変更するのに対し、sorted()は配列そのものは変更を加えず、新しく配列を作り、返します。shuffle()shuffled()の違いも同様です。
reversed()は、、、見なかったことにしたい。。。reversed()は実際に要素が逆順になったArrayを生成せず、そのようにふるまうReversedCollectionという型のインスタンスを返します。
参考記事を貼っておきます(reversed()の参考記事)

シーケンス操作

filter

条件を満たすものだけを取り出し、新しい配列を作ることができる。
たとえば、100までの正の整数で13または43の倍数であるものを含む配列を作ってみます。

let p = [Int](1...100)
let q = p.filter {
    $0 % 13 == 0 || $0 % 43 == 0
}
print(q) // [13, 26, 39, 43, 52, 65, 78, 86, 91]

クロージャの詳しい解説はしませんが、$0に1から100まで順番に入っていきます。初めは1が入り、{}のなかの条件式$0 % 13 == 0 || $0 % 43 == 0はfalseになるので、1は作成される配列の中に入りません。次に2が入り、、、というふうに一つ一つの要素が条件を満たすか確認していき、条件を満たす(trueを返す)要素のみの配列を新しく作成し、定数qに格納しています。

map

配列の各要素に指定した処理を適応させ、その結果からなる新しい配列を作成することができる。
たとえば、次のように配列rの全ての要素を2倍した配列を新たに作りたいとします。

let r = [10, 20, 30, 40]
let s = r.map {
    $0 * 2
}
print(s) // [20, 40, 60, 80]

$0に10から40まで順番に入っていき、条件式$0 * 2で全ての配列の要素を2倍しています。その配列を定数sに格納しています。
たとえば、次のようにfilterを合わせて使うこともできます。

let t = ["佐々木", "田中", "山本", "大和田"]
let u = t.map {
    $0 + "さん"
}.filter {
    $0.count >= 5
}
print(u) // ["佐々木さん", "大和田さん"]

まず、mapで配列tの各要素に"さん"をつけています。mapによって新たに作られた配列に、さらにfilterで文字数が5以上になった場合の配列を新たに作っています。その新たに作られた配列を定数uに格納しています。

mapValues

mapの返り値は辞書も集合も要素が格納された配列です。ただし、辞書については、含まれる要素の値に対して処理を適応し、その結果としてキーを組み合わせて新しい辞書を返すメソッドが用意されています。

let steps = [90, 80, 70, 60]
let evals = ["秀", "優", "良", "可", "不可"]
let v = ["太郎": 68, "二郎": 50, "三郎": 62, "四郎": 90]
let marks = v.mapValues { (point: Int) -> String in
    var index = 0
    for step in steps {
        if point >= step {
            break
        }
        index += 1
    }
    return evals[index]
}
print(marks) //たとえば["二郎": "不可", "四郎": "秀", "太郎": "可", "三郎": "可"]

ただし、辞書は配列のように順番通りに出力されないことに注意してください。今回の場合、vが辞書で、mapValuesしたものを定数marksに格納しているので、print(marks)すると順番が変わることがあります。

compactMap

mapやmapValueはどちらも処理前と処理後の一対一に対応しています。しかし、目的によってはfilterのように不要な要素を取り除きたいということもあります。そのような時に用いられるシーケンスのメソッドがcompactMapです。

let people = [
    ["Name": "れおん", "Pet": "ハリネズミ", "BloodType": "O"],
    ["Name": "承太郎", "BloodType": "B"],
    ["Name": "花京院", "身長": "178cm"],
    ["Name": "ポルナレフ", "Pet": "イギー", "BloodType": "AB"]
]
let pets = people.compactMap {
    $0["Pet"]
}
print(pets) // ["ハリネズミ", "イギー"]

承太郎と花京院は"Pet"を持っていないのでcompactMapの条件式$0["Pet"]がnilになり、ハリネズミとイギーが入った配列が新たに作られています。

flatMap

flatMapはすべての要素をシーケンスへと変換し、さらに、 それを1つのシーケンスに連結します。

let bookLists = [
    "Aさん": ["本1", "本2"],
    "Bさん": [],
    "Cさん": ["本3"]
]
let books = bookLists.flatMap {
    $0.value
}
print(books) // たとえば["本3", "本1", "本2"]

reduce

シーケンスに含まれる要素を一つずつ全て使って処理を行い、その結果として一つの値を返します。
たとえば、数値の列の合計を求めるには次のようにします。

let w = [1, 2, 5, 1, 8, 2, 3]
let sum = w.reduce(0) {
    $0 + $1
}
print(sum) // 22

一周目は$0に第一引数にある初期値0が入り、$1には配列の初めの要素1が入ります。よって、0 + 11が得られる。
二周目は$0に先ほど一周目で得られた1が入り、$1には配列の二番目の要素2が入ります。よって、1 + 23が得られる。
三周目は$0に先ほど二周目で得られた3が入り、$1には配列の三番目の要素5が入ります。よって、3 + 58が得られる。
これを繰り返していくと、配列wの合計値が求められる。

forEach

配列の各要素に順番に適用していく。
for-in文と基本的には同じだが、forEachはbreakなどが使えない。

let numberWords = ["one", "two", "three"]
numberWords.forEach {
    print($0)
}
//"one"
//"two"
//"three"

enumerated

シーケンスから取り出した順の通し番号と要素からなるタプルを含むシーケンスを返す。

let names = ["名前A", "名前B", "名前C"]
for t in names.enumerated() {
    print(t)
}
//(offset: 0, element: "名前A")
//(offset: 1, element: "名前B")
//(offset: 2, element: "名前C")

タプルのラベルは無視しても良い。

zip

二つのシーケンスを引数としてそれぞれの要素をタプルとする新しいシーケンスを返します。

for t in zip("アイウエオ", [1, 2, 3]) {
    print(t)
}
//("ア", 1)
//("イ", 2)
//("ウ", 3)

repeatElement

引数で指定したインスタンスを指定した個数だけ含むシーケンスを作成して返します。

for t in zip(["佐々木", "田中", "佐藤"], repeatElement("Hello", count: 2)) {
    print(t)
}
//("佐々木", "Hello")
//("田中", "Hello")

sequence

最初から全ての要素が決まっているのではなく、必要が生じてから次の要素を計算して返すという、遅延評価によるシーケンスのインスタンス生成して返します。

var seq = sequence(first: 10000000, next: { $0 / 10 })
for p in seq.prefix(10) {
    print(p)
}
//10000000
//1000000
//100000
//10000
//1000
//100
//10
//1
//0
//0

lazy

遅延評価を行うとシーケンスの全要素を処理する必要がなくなったり、最初の結果をすばやく得たりできる場合があります。
lazyを利用して実行順序が変化する例を示します。

func f(_ n: Int) -> Bool {
    print(n, terminator: " ")
    return n & 1 == 0
}
(0...5).filter(f).forEach {
    print("[\($0)]", terminator: " ") // 0 1 2 3 4 5 [0] [2] [4]
}
(0...5).lazy.filter(f).forEach {
    print("[\($0)]", terminator: " ") // 0 [0] 1 2 [2] 3 4 [4] 5 
}

最後に

初めての投稿ですが、なんとか書き終えれてよかったです。
Swift難しい。。。

67
62
2

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
67
62