Search code examples
dictionarygostructldaptransform

Go - Idiomatic way to map one struct to another


I'm using a third party Go lang library to query a LDAP database of users. The library returns a SearchResult slice of ResultUser that I need to map to my own User struct. The two structs have different field names and I only need specific fields from the ResultUser. Is there a more idiomatic way in Go to transform one struct to another.

I've created a demo below (link also on Go Playground). Thanks in advance for any advice you can give to this Go newbie!

package main

import "fmt"

type (
    User struct {
        id        int32
        firstName string
    }

    ResultUser struct {
        uid   int32
        fname string
    }
    SearchResults []ResultUser
)

func main() {
    results := getSearchResults()
    users := mapResultsToUsers(results) // <-- This is the problem
    fmt.Println("User struct:", users[0].id, users[0].firstName)
    fmt.Println("User struct:", users[1].id, users[1].firstName)
}

// Simulates a query to a data with a library
func getSearchResults() (results SearchResults) {
    return append(results, ResultUser{1, "John"}, ResultUser{2, "Jane"})
}

// Seems like a code smell to have to do this
// Is there a more idiomatic way to do this?
func mapResultsToUsers(results SearchResults) (users []User) {
    for _, result := range results {
        users = append(users, User{result.uid, result.fname})
    }
    return users
}

I've seen struct field tags but not sure if there is a better way.


Solution

  • I think that what you've got is pretty much the best solution, although I would move the mapping into a dedicated function, some like:

    func fromResultUser(r *ResultUser) *User {
      return &User{
        id: r.uid,
        firstName: r.fname,
      }
    }
    

    Then mapResultsToUsers becomes:

    func mapResultsToUsers(results SearchResults) (users []*User) {
      for _, result := range results {
          users = append(users, fromResultUser(result))
      }
      return users
    }
    

    I've seen struct field tags but not sure if there is a better way.

    You could put together something so that you could annotate your User struct like:

    User struct {
        id        int32     `mappedFrom:"uid"`
        firstName string    `mappedFrom:"fname"`
    }
    

    But the method required to implement that would be substantially more complex than the fromResultUser presented here, and would involve becoming familiar with the reflect package. I would argue that, as a colleague of mine is fond of saying, "the juice isn't worth the squeeze".