【初心者】Go 言語で JavaScript の配列メソッドを再現する方法まとめ。

こんにちは、開発チームでエンジニアをしている和田です。

普段の業務ではバックエンドは Node.js 、フロントエンドは Vue.js を使って開発することが多く、JS・TSな毎日を過ごしています。 最近、N2i では「毎朝15分で Go 言語を習得する」という勉強会が開催されており、Go 言語について学ぶ機会が増えました。

というわけで、今回は Go 言語の初学者を対象に Go 言語のコンセプトを理解しつつ、JavaScript の配列のメソッドを Go 言語で再現する方法について紹介します💪

Go 言語 のコンセプトに触れる

Go 言語のコンセプトに触れる
Go 言語のコンセプトに触れる
People illustrations by Storyset

Go 言語というと、Google社が開発した「シンプルで効率的なコードが書けるプログラミング言語」というイメージが強いのではないでしょうか?

実際に書いてみると文法も簡単で覚えることも少なく、コンセプト通りシンプルなコードを書くことができます。

しかしながら、シンプルがゆえにビルトインのメソッドは少なく、JavaScript の配列で使用する map()filter() などのメソッドは用意されていません。

ループ系の処理は全て for のみによって記述します。

Go をうまく書くには、その特性やイディオムを理解することが重要です。また、名前付けや書式、プログラムの組み立て方など、Goでプログラミングするための定石を知ることで、他のGoプログラマーが理解しやすいプログラムを書くことができます。 Effective Go より

上述の通り、シンプルな文法をイディオマティックに使いこなすことがこの言語のカギと言えます。


JavaScript の配列メソッドを Go 言語で再現する

では、本題に参りましょう。ここからは JavaScript の各種配列メソッドを紹介しながら、それらを Go 言語で記述していきます。

実行環境は Go 言語 が v1.20.1 、Node.js が v16.15.0 です。

下記の6つのメソッドについてそれぞれ解説していきます。

  • map
  • filter
  • find
  • findIndex
  • every
  • some

map メソッド

JavaScript の map メソッドは、配列の要素に対して任意の処理を適応することができるメソッドです。 下記の JavaScript のコード では、int 型の配列の各要素をそれぞれ2倍にして返す処理が記述されています。

const numbers = [1, 2, 3, 4, 5];
const results = numbers.map((number) => number * 2);
console.log(results); // [ 2, 4, 6, 8, 10 ]

こちらのコードを Go 言語で実装すると下記のようになります。

package main

import "fmt"

type ints []int

func (numbers ints) Map(mapFunc func(number int) any) []any {
    var val []any
    for _, num := range numbers {
        val = append(val, mapFunc(num))
    }
    return val
}

func main() {
    numbers := ints{1, 2, 3, 4, 5}
    results := numbers.Map(func(num int) any { return num * 2 })
    fmt.Println(results...) // 2 4 6 8 10
}

処理の内容としては

  1. int型の要素で構成されたスライス ints 型を定義する
  2. ints 型に Map メソッドを実装する
  3. main 関数内で ints 型のスライスを宣言して、Map メソッドの返値を変数 results に格納する
  4. results の値を表示する

となっています。

Map メソッドは引数として、「int 型を受け取り任意の型で返す」関数 mapFunc を受け取り、対象の配列の各要素に mapFunc を実行し、val として返します。

呼び出し側で return num * 2 の箇所に要素に実行したい処理を書けば応用できますね。

filter メソッド

JavaScript の filter メソッドは、配列の各要素に対して、指定された条件に当てはまる要素のみを返すメソッドです。 下記の JavaScript のコードでは、偶数のみを返す処理が記述されています。

const numbers = [1, 2, 3, 4, 5];
const results = numbers.filter((number) => number % 2 === 0);
console.log(results); // [ 2, 4 ]

こちらを Go 言語で実装すると下記のようになります。

package main

import "fmt"

type ints []int

func (numbers ints) Filter(filterFunc func(number int) bool) []any {
    var val []any
    for _, num := range numbers {
        if filterFunc(num) {
            val = append(val, num)
        }
    }
    return val
}

func main() {
    numbers := ints{1, 2, 3, 4, 5}
    results := numbers.Filter(func(num int) bool { return num%2 == 0 })
    fmt.Println(results...) // 2 4
}

Map メソッドのときと同様に、独自で定義した ints 型に対して、Filter メソッドを実装しています。 filterFunc は「int 型を受け取りbool型を返す」関数です。この filterFunctrue を返すものだけを新たに作成するスライスに格納して返す処理を行います。

find メソッド

続いて find メソッドです。

JavaScript の find メソッドは、配列の要素の中で、指定された条件に当てはまる最初の要素を返すメソッドです。 下記のコードで、最初の偶数を返す処理を記述しています。

const numbers = [1, 2, 3, 4, 5];
const result = numbers.find((number) => number % 2 === 0);
console.log(result); // 2

こちらを Go 言語で書くと...

package main

import "fmt"

type ints []int

func (numbers ints) Find(findFunc func(number int) bool) int {
    var val int
    for _, num := range numbers {
        if findFunc(num) {
            val = num
            break
        }
    }
    return val
}

func main() {
    numbers := ints{1, 2, 3, 4, 5}
    result := numbers.Find(func(num int) bool { return num%2 == 0 })
    fmt.Println(result) // 2
}

filter メソッドと似ていますね。

ポイントは、Find メソッドの中で for ループを実行し、findFunc の条件に当てはまったら break を使ってループを抜け出す点です。

この処理によって、条件に当てはまる最初の要素のみを返す事ができます。

findIndex メソッド

JavaScript の findIndex メソッドは、配列の要素の中で、指定された条件に当てはまる最初の要素のインデックスを返すメソッドです。

下記のコードで、最初の3の倍数のインデックスを返します。実行結果は2になりますね。

const numbers = [1, 2, 3, 4, 5];
const result = numbers.findIndex((number) => number % 3 === 0);
console.log(result); // 2

こちらも Go 言語で書くと...

package main

import "fmt"

type ints []int

func (numbers ints) FindIndex(findFunc func(number int) bool) int {
    var val int
    for i, num := range numbers {
        if findFunc(num) {
            val = i
            break
        }
    }
    return val
}

func main() {
    numbers := ints{1, 2, 3, 4, 5}
    result := numbers.FindIndex(func(num int) bool { return num%3 == 0 })
    fmt.Println(result) // 2
}

Find メソッドと異なる点は、for ループでインデックス i を使用して、findIndexFunc の条件に当てはまる場合にインデックスを返す点です。 ほとんど同じ記述なので簡単ですね👍

every メソッド

JavaScript の every メソッドは、配列の全ての要素が指定された条件に当てはまるかどうかを真偽値で返すメソッドです。

下記のコードで、「全ての要素が偶数であるか?」・「全ての要素が10未満であるか?」を調べて真偽値で返しています。

const numbers = [1, 2, 3, 4, 5];
const resultA = numbers.every((number) => number % 2 === 0);
const resultB = numbers.every((number) => number < 10);
console.log(resultA); // false
console.log(resultB); // true

Go 言語で書くと下記のようになります。

package main

import "fmt"

type ints []int

func (numbers ints) Every(everyFunc func(number int) bool) bool {
    val := true
    for _, num := range numbers {
        if !everyFunc(num) {
            val = false
            break
        }
    }
    return val
}

func main() {
    numbers := ints{1, 2, 3, 4, 5}
    resultA := numbers.Every(func(num int) bool { return num%2 == 0 })
    resultB := numbers.Every(func(num int) bool { return num < 10 })
    fmt.Println(resultA) // false
    fmt.Println(resultB) // true
}

Every メソッドの中で、最初に返値 valtrue としておき、everyFunc の条件に当てはまらないものが1つでもあった場合ループを抜けて false で返します。

some メソッド

最後に、some メソッドです。

JavaScript の some メソッドは every メソッドと対照的に、配列の要素が1つでも指定されたの条件に当てはまるかどうかを真偽値で返すメソッドです。

下記のコードで、「全ての要素が偶数であるか?」・「全ての要素が10より大きいか?」を調べて真偽値で返しています。

const numbers = [1, 2, 3, 4, 5];
const resultA = numbers.some((number) => number % 2 === 0);
const resultB = numbers.some((number) => number > 10);
console.log(resultA); // true
console.log(resultB); // false

Go 言語で書いてみましょう。

package main

import "fmt"

type ints []int

func (numbers ints) Some(someFunc func(number int) bool) bool {
    val := false
    for _, num := range numbers {
        if someFunc(num) {
            val = true
            break
        }
    }
    return val
}

func main() {
    numbers := ints{1, 2, 3, 4, 5}
    resultA := numbers.Some(func(num int) bool { return num%2 == 0 })
    resultB := numbers.Some(func(num int) bool { return num > 10 })
    fmt.Println(resultA) // true
    fmt.Println(resultB) // false
}

Every メソッドと異なる点は、返値 valfalse としておき、someFunc の条件に当てはまるものが1つでもあった場合ループを抜けて true で返します。 everyFunc とまったく逆の処理ですね。

【まとめ】Go 言語はイディオムの理解が大切

最後まで読んでくださりありがとうございました!

以上が、JavaScript の各種配列メソッドを Go 言語で実装した解説です。

今回は普段良く使う JavaScript の配列メソッドを中心に Go 言語で再現してみました。

JavaScript の配列メソッドは他にもありますし、Go 言語での再現方法も様々なやり方があると思います。

シンプルがゆえに長いコードとなりましたが、何より読みやすくどんな処理を実行するのかハッキリわかるのが Go 言語のいいところですね。

「〇〇系の処理をするときはこの書き方だ!🤓」

といったように、イディオム的記法を体で覚えてしまえばスムーズに開発できそうなので、日々の学習を続けたいと思います🫡