Go言語で配列やスライスから要素を探す方法まとめ

Go

Go言語(Golang)で配列やスライスを使っていると、「特定の値が入っているか調べたい」「その値がどこにあるか知りたい」といった場面によく出くわします。

しかし、GoにはPythonやJavaScriptのような便利なindexOf関数が最初から用意されていません。そのため、検索の処理は自分で作る必要があります。

この記事では、Goで配列・スライスを検索する基本的な方法を、実際の例といっしょにていねいに説明します。

スポンサーリンク

配列とスライスの違いを確認しよう

配列とスライスってなに?

Go言語には、似ているけれど少し違う2つのデータ型があります。

  • 配列:大きさが決まっている。プログラムを作るときにサイズが決定される
  • スライス:大きさを変えられる。配列のように使えるが、より自由

Goでは実際のプログラム開発でスライスを使う場面が多いため、この記事でもスライスを中心に説明します。

実際に見てみよう

package main

import "fmt"

func main() {
    // 配列:サイズが3で固定
    arr := [3]int{1, 2, 3}
    
    // スライス:サイズを後から変えられる
    slice := []int{1, 2, 3, 4, 5}
    
    fmt.Println("配列:", arr)     // 配列: [1 2 3]
    fmt.Println("スライス:", slice) // スライス: [1 2 3 4 5]
}

検索処理は主にスライスに対して行うことが多いです。次では、値が含まれているかを確認する方法を紹介します。

値がスライスに含まれているかを確認する方法

基本的な考え方

Goには直接containsのような関数がないため、自分でループを使って実装します。ループでスライスの中身を一つずつ確認して、目的の値があるかどうかを調べます。

実際に作ってみよう

package main

import "fmt"

// 数値のスライスに特定の値が含まれているかチェックする関数
func contains(slice []int, target int) bool {
    for _, v := range slice {
        if v == target {
            return true  // 見つかったらtrueを返す
        }
    }
    return false  // 最後まで見つからなかったらfalseを返す
}

func main() {
    nums := []int{10, 20, 30, 40}
    
    // 30があるかチェック
    fmt.Println(contains(nums, 30))  // 出力: true
    
    // 50があるかチェック
    fmt.Println(contains(nums, 50))  // 出力: false
}

コードの説明

  • for _, v := range slice:スライスの要素を一つずつ取り出す
  • _:インデックス(位置)は使わないので_で無視
  • v:取り出した値
  • if v == target:取り出した値と探している値が同じかチェック

このように、シンプルなループで確認できます。次は、要素の位置(インデックス)を取得する方法を見ていきます。

要素の位置(インデックス)を取得する方法

なぜ位置を知りたいの?

値があるかどうかだけでなく、「何番目にあるか」を知りたい場合があります。たとえば、その位置の値を変更したり、その位置から別の処理をしたりするときに便利です。

実際に作ってみよう

package main

import "fmt"

// 数値のスライスで特定の値の位置を探す関数
func indexOf(slice []int, target int) int {
    for i, v := range slice {
        if v == target {
            return i  // 見つかったら位置を返す
        }
    }
    return -1  // 見つからなかった場合は-1を返す
}

func main() {
    nums := []int{100, 200, 300, 400}
    
    // 300の位置を探す
    position := indexOf(nums, 300)
    fmt.Println(position)  // 出力: 2(0から数えて2番目)
    
    // 500の位置を探す(存在しない)
    position2 := indexOf(nums, 500)
    fmt.Println(position2)  // 出力: -1(見つからない)
    
    // 実際に使ってみる
    if position != -1 {
        fmt.Printf("300は%d番目にあります\n", position)
    }
}

コードの説明

  • for i, v := range slice:今度は位置(i)も使う
  • return i:見つかったら位置を返す
  • return -1:見つからなかった場合は-1を返す(Goでよく使われる慣習)

検索だけでなく、位置を特定できるとより高度な処理ができるようになります。続いては、文字列スライスの検索方法を紹介します。

文字列スライスの検索

文字列も同じように検索できる

数値だけでなく、文字列のスライスも同じような方法で検索できます。プログラムの構造は基本的に同じです。

実際に作ってみよう

package main

import "fmt"

// 文字列のスライスに特定の文字列が含まれているかチェックする関数
func containsStr(slice []string, target string) bool {
    for _, v := range slice {
        if v == target {
            return true
        }
    }
    return false
}

// 文字列のスライスで特定の文字列の位置を探す関数
func indexOfStr(slice []string, target string) int {
    for i, v := range slice {
        if v == target {
            return i
        }
    }
    return -1
}

func main() {
    names := []string{"Alice", "Bob", "Charlie", "David"}
    
    // "Bob"があるかチェック
    fmt.Println(containsStr(names, "Bob"))     // 出力: true
    
    // "Bob"の位置を探す
    fmt.Println(indexOfStr(names, "Bob"))      // 出力: 1
    
    // "Eve"があるかチェック
    fmt.Println(containsStr(names, "Eve"))     // 出力: false
    
    // 実際の使用例
    searchName := "Charlie"
    if containsStr(names, searchName) {
        position := indexOfStr(names, searchName)
        fmt.Printf("%sは%d番目にいます\n", searchName, position)
    } else {
        fmt.Printf("%sは見つかりませんでした\n", searchName)
    }
}

型が違っても、基本的な構造は同じです。より便利な検索関数を作ることも可能です。

より便利な検索関数を作ってみよう

汎用的な関数の作成

毎回似たような関数を書くのは大変なので、いろいろなデータ型で使える関数を作ることもできます。

package main

import "fmt"

// 複数の条件で検索する関数
func findAll(slice []int, condition func(int) bool) []int {
    var result []int
    for _, v := range slice {
        if condition(v) {
            result = append(result, v)
        }
    }
    return result
}

func main() {
    nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    // 偶数だけを探す
    evenNums := findAll(nums, func(n int) bool {
        return n%2 == 0
    })
    fmt.Println("偶数:", evenNums)  // 出力: 偶数: [2 4 6 8 10]
    
    // 5より大きい数を探す
    bigNums := findAll(nums, func(n int) bool {
        return n > 5
    })
    fmt.Println("5より大きい数:", bigNums)  // 出力: 5より大きい数: [6 7 8 9 10]
}

よくある使用場面と実践例

ユーザー管理システムの例

package main

import "fmt"

type User struct {
    ID   int
    Name string
    Age  int
}

// ユーザーIDで検索
func findUserByID(users []User, id int) *User {
    for i, user := range users {
        if user.ID == id {
            return &users[i]  // 見つかったユーザーのポインタを返す
        }
    }
    return nil  // 見つからなかった場合
}

// 年齢で絞り込み
func findUsersByAge(users []User, minAge int) []User {
    var result []User
    for _, user := range users {
        if user.Age >= minAge {
            result = append(result, user)
        }
    }
    return result
}

func main() {
    users := []User{
        {ID: 1, Name: "田中", Age: 25},
        {ID: 2, Name: "佐藤", Age: 30},
        {ID: 3, Name: "鈴木", Age: 20},
    }
    
    // ID2のユーザーを探す
    user := findUserByID(users, 2)
    if user != nil {
        fmt.Printf("見つかったユーザー: %s\n", user.Name)
    }
    
    // 25歳以上のユーザーを探す
    adults := findUsersByAge(users, 25)
    fmt.Println("25歳以上のユーザー:")
    for _, u := range adults {
        fmt.Printf("- %s (%d歳)\n", u.Name, u.Age)
    }
}

よくある間違いと注意点

1. インデックスの範囲外アクセス

// ❌ 危険な例
func badExample(slice []int) {
    // スライスが空の場合、エラーになる
    fmt.Println(slice[0])
}

// ✅ 安全な例
func safeExample(slice []int) {
    if len(slice) > 0 {
        fmt.Println(slice[0])
    } else {
        fmt.Println("スライスが空です")
    }
}

2. nilスライスの処理

func safeContains(slice []int, target int) bool {
    if slice == nil {
        return false
    }
    
    for _, v := range slice {
        if v == target {
            return true
        }
    }
    return false
}

まとめ

Goでは、配列やスライスに対する検索処理を自分でループを使って書く必要があります。標準の関数がない分、検索のやり方を自分好みにカスタマイズしやすいとも言えます。

この記事で学んだポイント

  • スライスの要素を一つずつチェックする基本的な検索方法
  • 値の存在確認と位置取得の実装方法
  • 数値と文字列の両方での検索テクニック
  • より高度な検索条件の実装方法
  • 実際のプログラムでの活用例

コメント

タイトルとURLをコピーしました