3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Goで[]interface{}型のスライスを型ごとに分配する

Last updated at Posted at 2019-07-29

GAE/Goを書いている時によく遭遇するので、リフレクションでかっこよく解決した。

やりたいこと

// いろんな型の構造体(のポインタ)が混ざったスライス
	list := []interface{}{
		&Dog{CanOte: true},
		&Cat{NowSleeping: true},
		&Shark{StarringMovie: true},
		&Dog{CanOte: false},
	}

// 型
type (
	Animals struct {
		Dogs   []*Dog
		Cats   []*Cat
		Sharks []*Shark
	}

	Dog struct {
		CanOte bool
	}

	Cat struct {
		NowSleeping bool
	}

	Shark struct {
		StarringMovie bool
	}
)


型を見て適切なフィールドにappendをしていきたい。

  • DogをAnimals.Dogsにappend
  • CatをAnimals.Catsにappend
  • SharkをAnimals.Sharksにappend

同じ挙動をするコードはこれ。


func appendEachObjects(animals Animals, list []interface{}) {
	for _, l := range list {
		switch m := l.(type) {
		case *Dog:
			animals.Dogs = append(animals.Dogs, m)
		case *Cat:
			animals.Cats = append(animals.Cats, m)
		case *Shark:
			animals.Sharks = append(animals.Sharks, m)
		}
	}
}

リフレクションで解決するには

さて、いちいちcaseを増やしていくのはだるいのでリフレクションで解決する方法を考えた。


func appendEachObjects(animals Animals, list []interface{}) Animals {

	defer func() {
		if e := recover(); e != nil {
			fmt.Printf("panic: %+v\n", e)
			fmt.Println("DataObjectsのフィールドの型がポインタのSliceではないと思います")
		}
	}()

	for _, i := range list {

		hpv := reflect.ValueOf(&animals)
		t := hpv.Elem().Type()
		n := t.NumField()
		for fieldIndex := 0; fieldIndex < n; fieldIndex++ {
			field := t.Field(fieldIndex)
			fieldType := field.Type
			if fieldType.Kind() != reflect.Slice {
				continue
			}
			match := fieldType.Elem().Elem() == reflect.TypeOf(i).Elem()
			if !match {
				continue
			}
			l := reflect.Append(hpv.Elem().Field(fieldIndex), reflect.ValueOf(i))
			hpv.Elem().Field(fieldIndex).Set(l)
		}
	}
	return animals
}

func Test_ObjectAppendEach(t *testing.T) {

	animals := Animals{}
	list := []interface{}{
		&Dog{CanOte: true},
		&Cat{NowSleeping: true},
		&Shark{StarringMovie: true},
		&Dog{CanOte: false},
	}
	actual := appendEachObjects(animals, list)

	if len(actual.Dogs) != 2 {
		t.Fatalf("犬が2匹ではなく%d", len(actual.Dogs))
	}

	if len(actual.Cats) != 1 {
		t.Fatalf("猫が1匹ではなく%d", len(actual.Cats))
	}

	if len(actual.Sharks) != 1 {
		t.Fatalf("サメが1匹ではなく%d", len(actual.Sharks))
	}

	if actual.Cats[0].NowSleeping != true {
		t.Fatalf("actual.Cats[0].NowSleepingがTrueではなく%T", actual.Cats[0].NowSleeping)
	}

}

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?