【golang】星座と血液型の組み合わせでランキングを生成するてきなアレ

2月 12, 2022

どもども、先ほどヨドバシカメラで爆買いをキメてきました。


最近Twitter上で"あなたと相性がいい星座と血液型の組み合わせTOP10″といった診断メーカー的なものをよく見ますね。


三連休の暇つぶしとして星座と血液型の組み合わせをランダムで抽出しランキング形式で表示するものを勉強中のGolangで作成した見ました。


今回はTOP10ではなく48通り全てをランキング形式で表示するように作成し、TOP1~48全てに対応できるようにしておきましょう。目指すは以下のような出力です。

それでは参ります!

コードの全貌と解説

まずはコードの全貌をお見せします。

package main

import (
	"fmt"
	"math/rand"
    "time"
)

func decideRanking(){
	ranks := chooseType()

	for _, rank := range ranks {
		fmt.Println(rank)
	}
}

func chooseType()[]string{
	
	combination := []string{}
	ranks := []string{}

	for len(combination) < 48 {
		seiza := []string{"牡羊座","牡牛座","双子座","蟹座","獅子座","乙女座","天秤座","蠍座","射手座","山羊座","水瓶座","魚座"}
		bloodtype := []string{"A","B","O","AB"}
	
		choiced_seiza := choice(seiza)
		choiced_bloodtype := choice(bloodtype)
		combo := fmt.Sprintf("%s:%s", choiced_seiza, choiced_bloodtype)
		
		if contains(combination,combo) == false{
			combination = append(combination,combo)
			rank := fmt.Sprintf("%v位<%s:%s>", len(combination), choiced_seiza, choiced_bloodtype)
			ranks = append(ranks,rank)
		}
	} 
	return ranks
}

func choice(s []string) string {
    rand.Seed(time.Now().UnixNano())
    i := rand.Intn(len(s))
    return s[i]
}

func contains(s []string, e string) bool {
	for _, v := range s {
		if e == v {
			return true
		}
	}
	return false
}

func main(){
	decideRanking()
}

それぞれの要素を解説していきます。


まずは星座と血液型をそれぞれランダムに選択し組み合わせを生成しなければなりません。そこで星座と血液型を格納したスライスからランダムに一つ選択するために実装したのが下記のchoice関数です。

func choice(s []string) string {
    rand.Seed(time.Now().UnixNano())
    i := rand.Intn(len(s))
    return s[i]
}

この関数に文字列が格納されたスライスを渡すと、その中からランダムに一つ選択してくれます。使用しているのはよくあるrandです。下記のように引数にスライスを渡すことで一つランダムに取得します。

combination := []string{}
	ranks := []string{}

	for len(combination) < 48 {
		seiza := []string{"牡羊座","牡牛座","双子座","蟹座","獅子座","乙女座","天秤座","蠍座","射手座","山羊座","水瓶座","魚座"}
		bloodtype := []string{"A","B","O","AB"}
	
		choiced_seiza := choice(seiza)
		choiced_bloodtype := choice(bloodtype)
		combo := fmt.Sprintf("%s:%s", choiced_seiza, choiced_bloodtype)

これで変数comboにランダムに選択された星座と血液型が組み合わされた文字列が格納されます。これをforでループさせてあらかじめ用意している空スライスcombinationに追加していきます。

しかし、ここで注意するべきことがあります。それは組み合わせは一意でなければならないということです。そこでスライスcombinationにすでに追加されている組み合わせは追加しないという処理が必要となります。

それを実現するのが下記のcontain関数です。

func contains(s []string, e string) bool {
	for _, v := range s {
		if e == v {
			return true
		}
	}
	return false
}

引数にスライスと文字列を渡し、文字列がスライスに含まれていればtrue,含まれていなければfalseを返す関数です。この関数を利用し、結果がfalseの場合のみスライスcombinationに組み合わせを追加するようにし、重複を避けます。

		if contains(combination,combo) == false{
			combination = append(combination,combo)
			rank := fmt.Sprintf("%v位<%s:%s>", len(combination), choiced_seiza, choiced_bloodtype)
			ranks = append(ranks,rank)
		}
	} 
	return ranks
}

もうひとつ存在するスライスranksには生成された組み合わせにランキング(○位)を追加した値を追加しています。順位はスライスcombinationの長さで算出しています。スライスcombinationにランキングも含めてしまうと全ての順位の全ての組み合わせが出力されるため、実現したい内容ではなくなってしまうので今回はスライスを二つ用意しました。

さらに注意すべき点があります。それは今回使用しているfor文が終了する条件です。組み合わせを生成し続けるとcontain関数はやがてtrueのみを出力し、各スライスへの追加はストップしますが正しく条件をしていしないと組み合わせを生成してtrue判定を行うという処理が無限に回り続けてしまいます。

そこで今回はスライスcombinationの長さが48を超えるまでfor文が続くようにしています。ここは正直もっといい方法がありそうな気がしていますが…。

for len(combination) < 48 {

ここまできたらスライスranksに格納された値を出力するだけです。今回はrangeを使用し単純に出力しています。

func decideRanking(){
	ranks := chooseType()

	for _, rank := range ranks {
		fmt.Println(rank)
	}
}

これで無事冒頭に紹介した通りの出力ができました。

まとめ

今回は最近Twitterでよく見る星座血液型ランキング的なものを作ってみました。


作ってみると意外と考慮するべき点が多く結構時間がかかってしまいましたが、かなりいい勉強になりました。


次は何を作ろうかな。最後まで読んでいただきありがとうございました。


↓オススメ教材

Golang

Posted by CY