Welcome to part 12 of the Go programming tutorial series, where we're going to cover looping/iterating in Go. Leading up to this, we've been working on a project that aggregates news. We're at the point where we have a list of sitemaps that we want to iterate over in order to visit.
In the Go language, there is only the "for" loop, there is no "while" statement, but we can still mimic what we might traditionally put in a while loop. To begin, let's show a basic for loop:
for i:=0; i<10; i++{ fmt.Println(i) }
Let's unpack what's going on here. for
begins the loop, i:=0
initializes the value of i
to 0, then i<10
is the check for loop to end, and then i++
simply increments i
by one each loop.
Full code:
package main import "fmt" func main() { for i:=0; i<10; i++{ fmt.Println(i) } }
In order for a for loop to make sense, you likely need a starting value, but you might not need to set the value in the for loop. For example, you could have i
defined before the loop, and then the incrementing could be done within the for loop:
package main import "fmt" func main() { i:=0 for i<10{ fmt.Println(i) i++ } }
Often, we actually want to just do something while
something is the case. To start, a common use of a basic while
loop is to do an infinite loop, something that just goes on forever. This is easy enough:
package main import "fmt" func main() { for { fmt.Println("Do stuff.") } }
That would just run forever. Another common use for a while
loop is to do something while
something is the case. We can still do this with a for loop and a break
statement. The idea of a break
statement is to get out of a loop, and then we can use conditional statements to check.
package main import "fmt" func main() { x := 5 for { fmt.Println("Do stuff.",x) x+=3 if x > 25 { break } } }
Here, we start with pre-defining x as 5, then we start an infinite loop, which just prints "Do stuff" and the current value of x. Then, in every loop, we increment x by 3. Finally, we use an if
statement to check if x is greater than 25. If it is, then we break
out of the loop. In this simple example, we're using x to iterate in the loop and the value of x to break the loop, but then this is very similar to
package main import "fmt" func main() { for x:=5; x<25; x+=3{ fmt.Println("Do stuff",x) } }
...but you don't need the check to be against the value of x. For example, we could do something like:
package main import "fmt" func main() { a := 3 for x:=5; a <25; x+=3{ fmt.Println("Do stuff",x) a+=4 } }
In this case, a
starts as 3, x
starts as 5. If a
is greater than 25, it the loop will break, otherwise add 3 to x
. Within the loop, we also increment a
by 4 every time, which is what will eventually break the loop.
There's certainly more we could cover on loops and definitely on conditionals, but I think we've satisfied enough about for loops for us to go ahead and loop through our list of sitemaps, so let's get back to that.
If you recall, our code up to this point is:
package main import ( "encoding/xml" "fmt" "io/ioutil" "net/http" ) type Sitemapindex struct { Locations []Location `xml:"sitemap"` } type Location struct { Loc string `xml:"loc"` } func (l Location) String() string { return fmt.Sprintf(l.Loc) } func main() { resp, _ := http.Get("https://www.washingtonpost.com/news-sitemap-index.xml") bytes, _ := ioutil.ReadAll(resp.Body) var s Sitemapindex xml.Unmarshal(bytes, &s) fmt.Println(s.Locations) }
At this point, s.Locations
is something we'd like to iterate over. Previously we looped really more by using a "while" sort of loop in every instance, where we looped while something was or wasn't the case. In our case here, we want to loop over this list of URLs in a more traditional sense. We can do this by using Go's range
. We use range
to iterate over elements in data structures. We can do this with our code by doing:
for _, Location := range s.Locations { fmt.Printf("%s\n", Location) }
The first value returned will be the index of the value, which we're designating to _
, since we aren't going to be using index for any reason. If we don't use the underscore here, and instead use a variable name like index
or something, Go will throw an error because we're not using the variable we defined. The next value from range will be the actual value from the data structure. We'll then just print it out for now. All together now:
package main import ( "encoding/xml" "fmt" "io/ioutil" "net/http" ) type Sitemapindex struct { Locations []Location `xml:"sitemap"` } type Location struct { Loc string `xml:"loc"` } func (l Location) String() string { return fmt.Sprintf(l.Loc) } func main() { resp, _ := http.Get("https://www.washingtonpost.com/news-sitemap-index.xml") bytes, _ := ioutil.ReadAll(resp.Body) var s Sitemapindex xml.Unmarshal(bytes, &s) //fmt.Println(s.Locations) for _, Location := range s.Locations { fmt.Printf("%s\n", Location) } }
Running this, you should now clearly get output of each url on a new line as we iterate over this list. Of course, our goal now is to visit these sitemaps, and actually grab article data, so that's what we'll do in the next tutorial!