| <!--{ |
| "Title": "Return greetings for multiple people" |
| }--> |
| |
| <p> |
| In the last changes you'll make to your module's code, you'll add support for |
| getting greetings for multiple people in one request. In other words, you'll |
| handle a multiple-value input, then pair values in that input with a |
| multiple-value output. To do this, you'll need to pass a set of names to a |
| function that can return a greeting for each of them. |
| </p> |
| |
| <aside class="Note"> |
| <strong>Note:</strong> This topic is part of a multi-part tutorial that begins |
| with <a href="/doc/tutorial/create-module.html">Create a Go module</a>. |
| </aside> |
| |
| <p> |
| But there's a hitch. Changing the <code>Hello</code> function's |
| parameter from a single name to a set of names would change the function's |
| signature. If you had already published the <code>example.com/greetings</code> |
| module and users had already written code calling <code>Hello</code>, that |
| change would break their programs.</p> |
| |
| <p> |
| In this situation, a better choice is to write a new function with a different |
| name. The new function will take multiple parameters. That preserves the old |
| function for backward compatibility. |
| </p> |
| |
| <ol> |
| <li> |
| In greetings/greetings.go, change your code so it looks like the following. |
| |
| <pre> |
| package greetings |
| |
| import ( |
| "errors" |
| "fmt" |
| "math/rand" |
| "time" |
| ) |
| |
| // Hello returns a greeting for the named person. |
| func Hello(name string) (string, error) { |
| // If no name was given, return an error with a message. |
| if name == "" { |
| return name, errors.New("empty name") |
| } |
| // Create a message using a random format. |
| message := fmt.Sprintf(randomFormat(), name) |
| return message, nil |
| } |
| |
| <ins>// Hellos returns a map that associates each of the named people |
| // with a greeting message. |
| func Hellos(names []string) (map[string]string, error) { |
| // A map to associate names with messages. |
| messages := make(map[string]string) |
| // Loop through the received slice of names, calling |
| // the Hello function to get a message for each name. |
| for _, name := range names { |
| message, err := Hello(name) |
| if err != nil { |
| return nil, err |
| } |
| // In the map, associate the retrieved message with |
| // the name. |
| messages[name] = message |
| } |
| return messages, nil |
| }</ins> |
| |
| // Init sets initial values for variables used in the function. |
| func init() { |
| rand.Seed(time.Now().UnixNano()) |
| } |
| |
| // randomFormat returns one of a set of greeting messages. The returned |
| // message is selected at random. |
| func randomFormat() string { |
| // A slice of message formats. |
| formats := []string{ |
| "Hi, %v. Welcome!", |
| "Great to see you, %v!", |
| "Hail, %v! Well met!", |
| } |
| |
| // Return one of the message formats selected at random. |
| return formats[rand.Intn(len(formats))] |
| } |
| </pre> |
| |
| <p> |
| In this code, you: |
| </p> |
| |
| <ul> |
| <li> |
| Add a <code>Hellos</code> function whose parameter is a slice of names |
| rather than a single name. Also, you change one of its return types from |
| a <code>string</code> to a <code>map</code> so you can return names |
| mapped to greeting messages. |
| </li> |
| <li> |
| Have the new <code>Hellos</code> function call the existing |
| <code>Hello</code> function. This helps reduce duplication while also |
| leaving both functions in place. |
| </li> |
| <li> |
| Create a <code>messages</code> map to associate each of the |
| received names (as a key) with a generated message (as a value). In Go, |
| you initialize a map with the following syntax: |
| <code>make(map[<em>key-type</em>]<em>value-type</em>)</code>. You have |
| the <code>Hellos</code> function return this map to the caller. For more |
| about maps, see <a href="https://blog.golang.org/maps">Go maps in |
| action</a> on the Go blog. |
| </li> |
| <li> |
| Loop through the names your function received, checking that each has a |
| non-empty value, then associate a message with each. In this |
| <code>for</code> loop, <code>range</code> returns two values: the index |
| of the current item in the loop and a copy of the item's value. You |
| don't need the index, so you use the Go blank identifier (an underscore) |
| to ignore it. For more, see |
| <a href="/doc/effective_go.html#blank">The blank |
| identifier</a> in Effective Go. |
| </li> |
| </ul> |
| </li> |
| |
| <li> |
| In your hello/hello.go calling code, pass a slice of names, then print the |
| contents of the names/messages map you get back. |
| |
| <p> |
| In hello.go, change your code so it looks like the following. |
| </p> |
| |
| <pre> |
| package main |
| |
| import ( |
| "fmt" |
| "log" |
| |
| "example.com/greetings" |
| ) |
| |
| func main() { |
| // Set properties of the predefined Logger, including |
| // the log entry prefix and a flag to disable printing |
| // the time, source file, and line number. |
| log.SetPrefix("greetings: ") |
| log.SetFlags(0) |
| |
| <ins>// A slice of names. |
| names := []string{"Gladys", "Samantha", "Darrin"}</ins> |
| |
| // Request greeting messages for the names. |
| messages, err := greetings.Hellos(names) |
| if err != nil { |
| log.Fatal(err) |
| } |
| // If no error was returned, print the returned map of |
| // messages to the console. |
| fmt.Println(messages) |
| } |
| </pre> |
| |
| <p> |
| With these changes, you: |
| </p> |
| |
| <ul> |
| <li> |
| Create a <code>names</code> variable as a slice type holding three |
| names. |
| </li> |
| <li> |
| Pass the <code>names</code> variable as the argument to the |
| <code>Hellos</code> function. |
| </li> |
| </ul> |
| </li> |
| |
| <li> |
| At the command line, change to the directory that contains hello/hello.go, |
| then use <code>go run</code> to confirm that the code works. |
| |
| <p> |
| The output should be a string representation of the map associating names |
| with messages, something like the following: |
| </p> |
| |
| <pre> |
| $ go run . |
| map[Darrin:Hail, Darrin! Well met! Gladys:Hi, Gladys. Welcome! Samantha:Hail, Samantha! Well met!] |
| </pre |
| > |
| </li> |
| </ol> |
| |
| <p> |
| This topic introduced maps for representing name/value pairs. It also |
| introduced the idea of preserving backward compatibility |
| by implementing a new function for new or changed functionality in a module. |
| For more about backward compatibility, see |
| <a href="https://blog.golang.org/module-compatibility">Keeping your modules |
| compatible</a>. |
| </p> |
| |
| <p>Next, you'll use built-in Go features to create a unit test for your code.</p> |
| |
| <p class="Navigation"> |
| <a class="Navigation-prev" href="/doc/tutorial/random-greeting.html" |
| >< Return a random greeting</a |
| > |
| <a class="Navigation-next" href="/doc/tutorial/add-a-test.html">Add a test ></a> |
| </p> |