PR

Go言語(Golang)の「slices」パッケージの使い方|サンプルコードで解説

2. 基礎

こんにちは。Tomoyuki(@tomoyuki65)です。

Go言語(Golang)には、配列に似ているデータ構造として、複数の値をまとめて扱うためのslice(スライス)があります。

そんなsliceに対して検索やソート、要素の追加・削除など、様々な操作を簡単に行うための標準ライブラリとして、「slices」パッケージがあります。

この記事では、そんな「slices」パッケージの使い方についてご紹介します。

 

Go言語(Golang)の「slices」パッケージの使い方|サンプルコードで解説

「slices」パッケージには様々な関数がありますが、まずはよく使われる可能性が高い以下の4つを覚えましょう。

 

Contains:指定した要素が存在するか調べる

sliceに対して、指定した要素が存在するかを調べたい場合は、Contains関数を利用するとチェック可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{10, 20, 30, 40, 50}

    // 30が存在するか調べる
    exists := slices.Contains(numbers, 30)
    fmt.Println(exists)
}

※Containsのパラメータに渡す値の型は合わせる必要あり。

 

実行結果

true

 

Index:要素のインデックスを調べる

sliceに対して、指定した要素のインデックスを調べたい場合は、Index関数を利用するとチェック可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{10, 20, 30, 40, 50}

    // 値が30のインデックスを取得する
    index := slices.Index(numbers, 30)
    fmt.Println(index)
}

※インデックスは0から始まり、要素が見つからない場合は-1を返す。

 

実行結果

2

 

Sort:昇順にソートする

sliceに対して、昇順にソートしたい場合は、Sort関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{30, 10, 50, 40, 20}
    fmt.Println(numbers)

    // 昇順にソートする
    slices.Sort(numbers)
    fmt.Println(numbers)
}

※数値は小さい順、文字列は辞書順に並び替えます。降順にしたい場合は、昇順にソート後にReverse関数で順番を逆にすると可能です。

 

実行結果

[30 10 50 40 20]
[10 20 30 40 50]

 

SortFunc:独自の条件でソートする

構造体のsliceなど、単純な昇順・降順では表現できない条件で並べ替えたい場合は、SortFunc関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

// ユーザー構造体の定義
type User struct {
    Name string
    Age int
}

func main() {
    // ユーザー作成
    users := []User{
        {Name: "田中", Age: 30},
        {Name: "佐藤", Age: 20},
        {Name: "石田", Age: 25},
    }
    fmt.Println(users)

    // 年齢の若い順にソートする
    slices.SortFunc(users, func(a, b User) int {
        if a.Age < b.Age {
            // aをbの前にする
            return -1
        }
        if a.Age > b.Age {
            // aをbの後ろにする
            return 1
        }
        // aとbは同じ順位
        return 0
    })

    fmt.Println(users)
}

※SortFunc(スライス, 比較関数)の形式で使います。戻り値の値によってaとbを並べ替えます。

 

実行結果

[{田中 30} {佐藤 20} {石田 25}]
[{佐藤 20} {石田 25} {田中 30}]

 

スポンサーリンク

slice(スライス)の基礎操作も合わせて覚える

次にslice(スライス)の基礎操作に関する関数についても合わせて覚えましょう。

 

Delete:指定した範囲の要素を削除する

sliceに対して、指定した範囲の要素を削除したい場合は、Delete関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{10, 20, 30, 40, 50}

    // インデックス1から3の手前まで削除する
    numbers = slices.Delete(numbers, 1, 3)
    fmt.Println(numbers)
}

※元のスライスの内容が変更されるため、基本的に戻り値を同じ変数に代入します。

 

実行結果

[10 40 50]

 

Insert:指定した位置に要素を追加する

sliceに対して、指定した位置に要素を追加したい場合は、Insert関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{10, 30, 40}

    // インデックス1の位置に、値「20」を追加する
    numbers = slices.Insert(numbers, 1, 20)
    fmt.Println(numbers)
}

※対象のインデックスの位置に値を挿入し、元からあった値は後ろにずれます。

 

実行結果

[10 20 30 40]

 

Equal:2つのスライスが等しいか比較する

sliceに対して、2つのスライスが等しいか比較したい場合は、Equal関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    a := []int{10, 20, 30}
    b := []int{10, 20, 30}
    c := []int{30, 20, 10}

    // 比較結果をログ出力
    fmt.Println(slices.Equal(a, b))
    fmt.Println(slices.Equal(a, c))
}

※スライスの要素と順番が全て一致すればtrueを返します。尚、比較するスライスの型は合わせる必要あり。

 

実行結果

true
false

 

Clone:スライスを複製する

sliceをコピーしたい場合は、Clone関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    original := []int{10, 20, 30}

    // スライスをコピーする
    copied := slices.Clone(original)

    // コピーしたスライスの値を変更する
    copied[0] = 100

    // ログ出力
    fmt.Println(original)
    fmt.Println(copied)
}

※ポインタをコピーした場合、アドレスがコピーされ、元の値を参照するので注意。新しいポインタとして深いコピー(Deep Copy)を簡単にできる関数はないため、その場合は別途新しいポインタを作る処理をすること。

 

実行結果

[10 20 30]
[100 20 30]

 

スポンサーリンク

必要に応じて応用的な操作を覚える

その他、必要に応じて応用的な操作に関する関数についても覚えたい場合は以下の通りです。

 

ContainsFunc:条件を満たす要素があるか調べる

sliceに対して、条件を満たす要素があるかを調べたい場合は、ContainsFunc関数を利用するとチェック可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{1, 3, 5, 8, 9}

    // 偶数が存在するか調べる
    exists := slices.ContainsFunc(numbers, func(n int) bool {
        return n%2 == 0
    })
    fmt.Println(exists)
}

※条件を満たす要素が1つでもあればtrueを返す。Containsは特定の値を探すのに対し、ContainsFuncは条件で探せる。

 

実行結果

true

 

Reverse:要素の順番を逆にする

sliceに対して、要素の順番を逆にしたい場合は、Reverse関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{30, 10, 50, 40, 20}
    fmt.Println(numbers)

    // 降順にソートする
    slices.Sort(numbers)
    slices.Reverse(numbers)
    fmt.Println(numbers)
}

 

実行結果

[30 10 50 40 20]
[50 40 30 20 10]

 

IndexFunc:条件を満たす最初の要素の位置を調べる

sliceに対して、条件を満たす最初の要素の位置を調べたい場合は、IndexFunc関数を利用するとチェック可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{1, 3, 5, 8, 10}

    // 5より大きい最初の要素を探す
    index := slices.IndexFunc(numbers, func(n int) bool {
        return n > 5
    })

    // 存在チェック
    if index >= 0 {
        fmt.Println(index)
        fmt.Println(numbers[index])
    } else {
        fmt.Println("対象の要素は存在しませんでした。")
    }
}

※条件に一致する要素が見つからない場合は-1を返す。

 

実行結果

3
8

 

Chunk:スライスを一定件数ごとに分割する

sliceを指定した件数ごとに分割して処理したい場合は、Chunk関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{1, 2, 3, 4, 5, 6, 7}

    // 3件ずつに分割して処理する
    for chunk := range slices.Chunk(numbers, 3) {
        fmt.Println(chunk)
    }
}

※Chunkはスライスを指定した件数ごとに分割してイテレータ(データを1つずつ順番に取り出す仕組み)として返します。

 

実行結果

[1 2 3]
[4 5 6]
[7]

 

Max / Min:最大値・最小値を取得する

sliceの要素の最大値・最小値を取得したい場合は、Max / Min関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{30, 10, 50, 20, 40}

    fmt.Println(slices.Max(numbers))
    fmt.Println(slices.Min(numbers))
}

※空のスライスを渡すとpanicが発生するので注意。数値の大小や文字列の辞書順など、型に応じた順序で比較する。

 

実行結果

50
10

 

Compact:連続する重複要素をまとめる

sliceの連続する重複要素をまとめたい場合は、Compact関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{1, 1, 2, 2, 2, 3, 1, 1}

    numbers = slices.Compact(numbers)
    fmt.Println(numbers)
}

※全ての重複要素を削除するわけではないので注意。元のスライスの要素を変更し、重複を除去した長さのスライスを返します。

 

実行結果

[1 2 3 1]

 

BinarySearch:ソート済みスライスを二分探索する

ソート済みのsliceから対象の要素を二分探索したい場合は、BinarySearch関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    // ソート済みのスライス
    numbers := []int{10, 20, 30, 40, 50}

    // 要素「30」を二分探索で探す
    index, found := slices.BinarySearch(numbers, 30)

    // 結果をログ出力
    fmt.Println(index)
    fmt.Println(found)
}

※対象の要素がない場合は、その要素を挿入すると昇順を保てる位置のインデックスを返す。

 

実行結果

2
true

 

Replace:指定した範囲の要素を置き換える

sliceに対して、指定した範囲の要素を別の値に置き換えたい場合は、Replace関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{1, 2, 3, 4, 5}

    // インデックスが1から3の手前までを、8と9に置き換える
    numbers = slices.Replace(numbers, 1, 3, 8, 9)
    fmt.Println(numbers)
}

※インデックスを指定する際に開始位置は含むが、終了位置は含まないので注意。置き換える要素数は、元の範囲の要素数と同じでなくてもよい。

 

実行結果

[1 8 9 4 5]

 

Sorted:イテレータから新しいソート済みスライスを作る

イテレータから新しいソート済みスライスを作りたい場合は、Sorted関数を利用すると可能です。

package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{5, 2, 4, 1, 3}
    fmt.Println(numbers)

    // スライスをイテレータに変換
    iter := slices.Values(numbers)

    // イテレータから昇順にソートした新しいスライスを作成
    result := slices.Sorted(iter)
    fmt.Println(result)
}

※元のスライスは変更されない。

 

実行結果

[5 2 4 1 3]
[1 2 3 4 5]

 

スポンサーリンク

最後に

今回は「slices」パッケージの使い方についてご紹介しました。

利用できる関数が多く、スライスに対して様々な操作が可能ですが、全てを一気に覚える必要はありません。

まずはよく使われる可能性がある4種類や、基礎操作に関する4種類を知っておけば十分そうなので、これから学ぶ方はぜひ参考にしてみて下さい。

 

SubstackでITエンジニア向けニュースレター(メルマガ)を始めました!
こんにちは。Tomoyuki(@tomoyuki65)です。最近はどのSNSもアルゴリズム(どの投稿を誰に表示するかを決める仕組み)中心のタイムライン(投稿が流れてくる画面)になっていて、フォローしている人の投稿が見つけにくく、逆にフォロー...

 

この記事を書いた人
Tomoyuki

SE→ブロガーを経て、現在はSoftware Engineer(Web/Gopher)をしています!

Tomoyukiをフォローする
2. 基礎
スポンサーリンク
Tomoyukiをフォローする

コメント

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