1-2 配列や辞書などの基本的な操作

配列とは

配列とは、
[“Apple”, “Banana”, “Orange”]

[10, 8, 77, 50, 99]
のように、[ ]で囲まれた要素群のこと。

配列の定義方法

let 配列名: [Int] =  [要素1, 要素2, 要素3, ……]
let fruits: [String] = ["Apple", "Banana", "Orange"]

のように定義ができます。
Swiftは型推論してくれるので、

let 配列名 = [要素1, 要素2, 要素3, ……]
let fruits = ["Apple", "Banana", "Orange"]

と定義することがほとんどです。
ただし、要素が空の場合には型アノテーションを付けて、型を明示する必要がります。

let a = [] //コンパイルエラー
let b: [Int] = []

配列の操作

以下の配列を操作していきます。

let fruits = ["Apple", "Banana", "Orange"]

是非、実際に操作しながら学んでいきましょう。
以下でSwiftのブラウザ実行環境が使えます。
https://paiza.io/ja/projects/new

要素のアクセス

let fruits = ["Apple", "Banana", "Orange"]

let firstFruit = fruits[0] // 配列の最初の要素にアクセス
let lastFruit = fruits.last // 配列の最後の要素にアクセス
print(firstFruit) // Apple
print(lastFruit) // Optional("Orange")

なぜ、print(lastFruit)が、Orangeではなく、Optional(“Orange”) となってしまうのでしょうか。

Swiftの配列の「last」プロパティは、Optional型(オプショナル型)を返します。

これは、配列が空でない場合には最後の要素を包む「Optional型」として返され、空の場合にはnilとなります。

これはSwiftの安全性の配慮みたいなもので、配列が空であるかどうかを確認せずに「last」を使用しても安全にコードを書けるようにするためです。

Optional型を使用することで、プログラマーが空の配列を扱う場合に備えて条件分岐などを行うことができます。

Optional(“Orange”) から、Orangeだけを取得するには以下のようにします。

let fruits = ["Apple", "Banana", "Orange"]
let lastFruit = fruits.last // 配列の最後の要素にアクセス

if let unwrappedLastFruit = lastFruit {
    print(unwrappedLastFruit) // Orange
} else {
    print("nilだった時の処理")
}

ちなみに、「nil」とは・・・
多くのプログラミング言語で使用される特殊な値で、変数やオブジェクトが値を持っていないことを示します。

要素の追加と削除

var fruits = ["Apple", "Banana", "Orange"] // let → var に変更

fruits.append("Grapes") // 配列の末尾に要素を追加
print(fruits) // ["Apple", "Banana", "Orange", "Grapes"]

fruits += ["Pineapple", "Melon"] // 配列に複数の要素を追加
print(fruits) // ["Apple", "Banana", "Orange", "Grapes", "Pineapple", "Melon"]

fruits.remove(at: 1) // 指定したインデックスの要素を削除
print(fruits) // ["Apple", "Orange", "Grapes", "Pineapple", "Melon"]

削除系のメソッドは、removeの他にも色々あります。

  • remove … 指定の要素のインデックスを指定して削除する
  • removeFirst … 最初の要素を削除する
  • removeLast … 最後の要素を削除する
  • removeAll … すべての要素を削除する

さて、ここで「インデックス」という単語が出てきました。
インデックス(index)は、プログラミングにおいて非常に重要な概念です。

インデックスは、要素の位置を示す数字です。
インデックスは、0から採番され、1、2、3…と続きます。

例えば、配列arrにおける3番目の要素にアクセスするには、arr[2]となります。

let arr = [1, 5, 8]
1のindexは0
5のindexは1
8のindexは2

要素の更新

var fruits = ["Apple", "Banana", "Orange"]

fruits[0] = "Cherry" // 指定したインデックスの要素を更新
print(fruits) // ["Cherry", "Banana", "Orange"]

要素の挿入

var fruits = ["Apple", "Banana", "Orange"]

fruits.insert("Mango", at: 2) // 指定したインデックスに要素を挿入
print(fruits) // ["Apple", "Banana", "Mango", "Orange"]

配列の反復処理

for文

let fruits = ["Apple", "Banana", "Orange"]

for fruit in fruits {
    print(fruit)
}

// Apple
// Banana
// Orange

foreach文

let fruits = ["Apple", "Banana", "Orange"]

fruits.forEach { fruit in
    print(fruit)
}

// Apple
// Banana
// Orange

配列の長さ

let fruits = ["Apple", "Banana", "Orange"]

let count = fruits.count // 配列の要素数を取得
print(count) // 3

配列のフィルタリング

let fruits = ["Apple", "Banana", "Orange"]

// 条件に合致する要素をフィルタリング
let filteredFruits = fruits.filter { (fruit: String) -> Bool in
    return fruit.count > 5 // 5文字以上の要素ならばtrue
}

print(filteredFruits) // ["Banana", "Orange"]

「>」これ、比較演算子って言います。

比較演算子については、以下の記事にまとめましたので、よかったら参考にしてみてください。

配列のマッピング

let fruits = ["Apple", "Banana", "Orange"]

// 要素を変換して新しい配列を作成
let uppercaseFruits = fruits.map { (fruit: String) -> String in
    return fruit.uppercased() // 要素を大文字変換する
}

print(uppercaseFruits) // ["APPLE", "BANANA", "ORANGE"]

配列の結合

let fruits = ["Apple", "Banana", "Orange"]

let moreFruits = ["Kiwi", "Grapes"]
let combinedFruits = fruits + moreFruits // 配列を結合して新しい配列を作成

print(combinedFruits) // ["Apple", "Banana", "Orange", "Kiwi", "Grapes"]

配列の要素を指定された区切り文字で結合

let fruits = ["Apple", "Banana", "Orange"]

let joinedString = fruits.joined(separator: ", ")
print(joinedString) // Apple, Banana, Orange

配列の要素を昇順にソート

let fruits = ["Apple", "Banana", "Orange", "Kiwi", "Grapes"]

let sortedFruits = fruits.sorted()
print(sortedFruits) // ["Apple", "Banana", "Grapes", "Kiwi", "Orange"]

fruits 配列の要素がアルファベット順にソートされた新しい配列が作成されます。

(原本の fruits 配列は変更されません。)

ここで、ポロッと、「原本の fruits 配列は変更されません。」 と言っているのについて補足です。

破壊的メソッド非破壊的メソッド について

破壊的メソッドと、非破壊的メソッドとは、データの扱い方に関連する概念です。

「原本の fruits 配列は変更されません。」とは、「配列に対して、何か処理を施すけど、その配列自体に変更は加えませんよ、配列を破壊しませんよ。」と言い換えることができます。
これを、非破壊的メソッドといいます。

一方、「配列に対して、何か処理を施す際に、その配列自体に変更を加えますよ、配列を破壊しちゃいますよ。」これが、破壊的メソッドです。

Swiftの配列において、appendremove破壊的メソッドで、
filtermapは、非破壊的メソッドです。

例: 破壊的メソッド

var numbers = [1, 2, 3, 4, 5]

// appendは元の配列を変更する(破壊的)
numbers.append(6)

print(numbers)  // [1, 2, 3, 4, 5, 6]

例:非破壊的メソッド

let numbers = [1, 2, 3, 4, 5]

// // mapは新しい配列を生成し、元の配列は変更されない
let doubledNumbers = numbers.map({ (number: Int) -> Int in
    return number * 2
})

print(doubledNumbers)  // [2, 4, 6, 8, 10]
print(numbers) // [1, 2, 3, 4, 5] // 元の配列は変更されていない

配列が空かどうかをチェックする

let fruits = ["Apple", "Banana", "Orange"]
if fruits.isEmpty {
    print("空です😗")
} else {
    print("空じゃないです😌")
}
// 空じゃないです😌

その他の配列っぽいデータ構造

配列(Array)に似ている、データー構造の型をご紹介します。
そのために、まずはArray型の説明をおさらいです。

Array

  • Swiftの標準的な配列型です。
  • 要素の順序を持ち、同じ型の要素を格納できます。
  • 要素の順序が重要で、インデックスを使用して要素にアクセスします。
let fruits: [String] = ["Apple", "Banana", "Orange"]

Array型に似ている型はいくつかあり、代表的な型を紹介します。

Set

  • 要素の順序がなく、重複を許さない集合型です。
  • Setは要素の順序が保証されていないため、インデックスを使ったアクセスや順序に依存する処理は行えません。
  • 要素の追加や削除が高速であり、特定の順序でアクセスする必要がない場合に適しています。
let uniqueNumbers: Set<Int> = [1, 2, 3, 4, 5]

Dictionary

  • キーと値のペアを格納する辞書型です。
  • キーを使用して値にアクセスできます。
  • 順序が保証されていませんが、キーを使用して要素にアクセスすることができます。
let personInfo: [String: Any] = ["name": "John", "age": 30, "isStudent": false]

値の取り出し方は、

personInfo["age"] // Optional(30)

ArraySlice

  • Arrayの一部を表す型です。
  • Arrayから特定の範囲を切り出したり、操作したりするのに使用されます。
let array = [1, 2, 3, 4, 5]
let slice = array[1..<4] // 2, 3, 4

まとめ

ごめんなさい。 ちょっと本章、盛りだくさんになってしまいました。

配列は大事な概念だけど覚えるというより、「こういうものがある」と、把握することが大事!

焦らずやっていきましょう。

まとめ → 配列は盛りだくさん

ドリル

プログラムの実行環境が無い人は、こちらのサイトで動作確認ができます。

https://online.swiftplayground.run/

問題1

var fruits = ["Apple", "Banana", "Orange"]

問題2

if !fluets.isEmpty {
    print(fluets[2])
} else {
    fruits.append("Grape")
}