dth2331 2017-05-10 13:09
浏览 68
已采纳

Golang和结构数组的结构

I have the following structs defined for movies and TV shows:

type Movie struct {
    ID      string `json:"id"`
    Viewers int    `json:"count"`
}
type TVShow struct {
    ID      string `json:"id"`
    Season  int    `json:"season"`
    Episode int    `json:"episode"`
    Viewers int    `json:"count"`
}

Then I have the following structs that contain several movies or TV shows by country:

type Movies struct {
    PenultimateMonth map[string][]Movie
    LastMonth        map[string][]Movie
}
type TVShows struct {
    PenultimateMonth map[string][]TVShow
    LastMonth        map[string][]TVShow
}

Finally, I have a data struct that holds everything:

type Data struct {
    Movies  Movies
    Seasons Seasons
}

What I need to do is collect all IDs of all movies and TV shows from penultimate and last month.

I figured out that I can use reflection for that, but I only managed to iterate over each Data element individually instead of all:

func GetIDs(data *Data, country string) []string {
    var ids []string

    movies := reflect.ValueOf(data.Movies).Elem()
    tvShows := reflect.ValueOf(data.TVShows).Elem()

    for i := 0; i < movies.NumField(); i++ {
        moviesSubset := movies.Field(i).Interface().(map[string][]Movie)
        for _, movie := range moviesSubset[country] {
            ids = append(ids, movie.ID)
        }
    }

    for i := 0; i < tvShows.NumField(); i++ {
        tvShowsSubset := tvShows.Field(i).Interface().(map[string][]TVShow)
        for _, tvShow := range tvShowsSubset[country] {
            ids = append(ids, tvShow.ID)
        }
    }

    return ids
}

Is it possible to simplify the GetIDs function so that I don't need the two separate blocks for movies and TV shows, but only one to collect all IDs?

  • 写回答

1条回答 默认 最新

  • douying4909 2017-05-10 13:47
    关注

    Use an interface:

    type identifiable interface {
        GetID() string
    }
    
    type identifiables interface {
        GetIDs() []string
    }
    

    Which you can implement like:

    func (m Movie) GetID() string { return m.ID }
    

    And use to collect IDs polymorphically.

    You could write it for each type, or make the maps store the interface and implement it once.

    type identifiable interface {
        GetID() string
    }
    
    type identifiables interface {
        GetIDs() []string
    }
    
    func (m Movie) GetID() string { return m.ID }
    
    type movies []Movie
    type moviesByCountry map[string]movies
    
    func (m movies) GetIDs() (ret []string) {
        for _, movie := range m {
            ret = append(ret, movie.GetID())
        }
        return
    }
    
    func (m moviesByCountry) GetIDs() (ret []string) {
        for _, slice := range m {
            ret = append(ret, slice.GetIDs()...)
        }
        return
    }
    
    func (d Data) GetCountryIDs(country string) []string {
        return gatherIDs(d.TVShows[country], d.Movies[country])
    }
    
    func gatherIDs(collections ...identifiables) (ret []string) {
        for _, collection := range collections {
            ret = append(ret, collection.GetIDs()...)
        }
        return
    }
    

    Here's a working playground example.

    Not very efficient but simple and consistent IMHO. If that's a problem it can be optimized by passing in an accumulator slice, but I would suggest an interface with a private method of you go that route.

    I think there's a reasonable case to be made for

    type identifiableByCountry interface {
        GetCountryIDs(string) []string
    }
    

    since both Data and the map(s) could implement that.

    If you prefer working with lambdas, you could also use something like:

    type collection interface {
        walk(func(identifiable))
    }
    

    and implement that on the collection types, using it as

    c.walk(func(o identifiable) {
        ids = append(ids, o.GetID())
    })
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥50 关于#html5#的问题:H5页面用户手机返回的时候跳转到指定页面例如(语言-javascript)
  • ¥15 无法使用此凭据登录,因为你的域不可用,如何解决?(标签-Windows)
  • ¥15 yolov9的训练时间
  • ¥15 二叉树遍历没有报错但无法正常运行
  • ¥15 在linux系统下vscode运行robocup3d上场球员报错
  • ¥15 Python语言实验
  • ¥15 SAP HANA SQL 增加合计行
  • ¥20 用C#语言解决一个英文打字练习器,有偿
  • ¥15 srs-sip外部服务 webrtc支持H265格式
  • ¥15 在使用abaqus软件中,继承到assembly里的surfaces怎么使用python批量调动