Golang for loop of certain part of map - go

Attempting to create a for loop for each part of a map.
map[
asn:AS10
time:1.428790768e+09
ipv4s:[
68.114.75.0/24
216.215.56.0/22
216.215.60.0/22]
ipv6s:[
2607:f3f8::/32
]]
The above is the map, I'd like to try create a for loop for each value in ipv4s.
I've attempted, but I'm clearly not doing it correctly as it's merely based off my php knowledge.:
for json_map["ipv4s"]{
//whatever
}
PHP version if anyone needs an example rather then me attempting to explain:
foreach($obj->ipv4s as $value) {
echo $value; // return an ip
}
Update
package main
import (
"fmt"
"net/http"
"os"
"encoding/json"
)
func main() {
response, err := http.Get("https://www.enjen.net/asn-blocklist/index.php?asn=" + os.Args[1] + "&type=json_split&api=1")
if err != nil {
fmt.Printf("%s", err)
os.Exit(1)
} else {
defer response.Body.Close()
dec := json.NewDecoder(response.Body)
if dec == nil {
panic("Failed to start decoding JSON data")
}
json_map := make(map[string]interface{})
err = dec.Decode(&json_map)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", json_map)
for i := range json_map {
for _, ip := range json_map[i]["ipv4s"] {
fmt.Printf(ip)
}
}
}
}

Effective Go is a good source once you have completed the tutorial for go.
There it is also described how one iterates over a slice:
for key, value := range json_map {
// ...
}
Or if you don't need the key:
for _, value := range json_map {
// ...
}
You might have to nest two loops, if it is a slice of maps:
for i := range json_map {
for _, ip := range json_map[i]["ipv4s"] {
// ...
}
}

Related

How can I destruct this in Golang?

It may be a stupid question because I just learned Golang. I hope you understand.
I am making a program to extract data from the homepage using the goquery package:
package main
import (
"fmt"
"log"
"net/http"
"github.com/PuerkitoBio/goquery"
)
var url string = "https://www.jobkorea.co.kr/Search/?stext=golang&tabType=recruit&Page_No=3"
func main() {
getPages()
}
func getPages() int {
res, err := http.Get(url)
checkErr(err)
checkCode(res)
defer res.Body.Close()
doc, err := goquery.NewDocumentFromReader(res.Body)
checkErr(err)
doc.Find(".tplPagination").Each(func(i int, s *goquery.Selection) {
fmt.Println(s.Find("a"))
})
return 0
}
func checkErr(err error) {
if err != nil {
log.Fatalln(err)
fmt.Println(err)
}
}
func checkCode(res *http.Response) {
if res.StatusCode != 200 {
log.Fatalln("Request failed with statusCode:", res.StatusCode)
}
}
It prints below:
&{[0x140002db0a0 0x140002db570 0x140002db810 0x140002dbd50 0x140002dc000 0x140002dc2a0 0x140002dc540 0x140002dc850] 0x140000b2438 0x14000305680}
&{[0x140002dcd90 0x140002dd810] 0x140000b2438 0x14000305710}
But I just want to print only the first array out. Like this:
[0x140002dcd90 0x140002dd810]
How can I destruct them?
The problem is that you are printing as result is matched.
You can save the *goquery.Selection in a new slice and print only the last element. This example is working because you want the last occurrence, but in real life you must parse the query result for something in specific to not depend about result order.
// type Selection struct {
// Nodes []*html.Node
// document *Document
// prevSel *Selection
// }
var temp []*goquery.Selection
temp = append(temp, doc.Find(".tplPagination").Each(func(i int, s *goquery.Selection) {
s.Find("a")
}))
fmt.Printf("last: %v\n", temp[len(temp)-1])
temp[len(temp)-1]: &{[0xc0002dcd90 0xc0002e0a80] 0xc00000e3f0 0xc000309770}
The Nodes []*html.Node can be accessed with same example:
fmt.Printf("last: %v\n", temp[len(temp)-1].Nodes)
As per your comment you were looking to parse the page and get the number of pages and number of posts. Here is my attempt:
package main
import (
"fmt"
"github.com/PuerkitoBio/goquery"
"log"
"math"
"net/http"
"strconv"
"strings"
)
func errCheck(err error) {
if err != nil {
log.Fatal(err)
}
}
func ExampleScrape() {
url := "https://www.jobkorea.co.kr/Search/?stext=golang&tabType=recruit&Page_No=%s"
page := 3
fmt.Println("Current page:", page)
res, err := http.Get(fmt.Sprintf(url, page))
errCheck(err)
defer res.Body.Close()
if res.StatusCode != 200 {
log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
}
doc, err := goquery.NewDocumentFromReader(res.Body)
errCheck(err)
posts_div := doc.Find(".recruit-info div.dev_list.lists-cnt")
total_count_div := posts_div.Nodes[0]
var total_count int
for _, a := range total_count_div.Attr {
if a.Key == "total-count" {
total_count, err = strconv.Atoi(a.Val)
errCheck(err)
break
}
}
fmt.Println("Total count:", total_count)
titles := posts_div.Find(".list-post .title")
fmt.Println("On this page:", len(titles.Nodes))
fmt.Println("Pages:", math.Ceil(float64(total_count)/float64(len(titles.Nodes))))
fmt.Println("\nTitles on this page:")
titles.Each(func(i int, s *goquery.Selection) {
fmt.Println("\t-", strings.TrimSpace(s.Text()))
})
}
func main() {
ExampleScrape()
}

RethinkDB r.DBList() gives blank object in return

I'm using this golang code to check list of databases in RethinkDB, and getting no lists in return.
package main
import (
"encoding/json"
"fmt"
"log"
r "gopkg.in/rethinkdb/rethinkdb-go.v6"
)
func main() {
log.SetFlags(0)
rdbOpts := r.ConnectOpts{
Address: "localhost:28015",
}
rconn, err := r.Connect(rdbOpts)
checkError(err)
res, err := r.DBList().Run(rconn)
checkError(err)
printObj(res)
}
func checkError(err error) {
if err != nil {
log.Println(err)
return
}
}
func printObj(v interface{}) {
vBytes, _ := json.Marshal(v)
fmt.Println(string(vBytes))
}
Result:
$ go run main.go
{}
This is just a fresh started Rethinkdb instance on local machine, which if queried through Data Explorer from web ui indeed returns the following answer for query r.dbList()
[
"rethinkdb" ,
"test"
]
What am I doing wrong in my query? I know it must be something small, as it's just basic query.
Appreciate any pointers or help. Thanks
I see what I was doing wrong. 🤦‍♂️
I didn't process the response to show rows data as per RethinkDb docs.
Here's the working main() func code:
func main() {
rdbOpts := r.ConnectOpts{
Address: "localhost:28015",
}
rconn, err := r.Connect(rdbOpts)
checkError(err)
res, err := r.DBList().Run(rconn)
checkError(err)
printObj(res)
var row []interface{}
err2 := res.All(&row)
if err2 == r.ErrEmptyResult {
// row not found
}
if err2 != nil {
// error
}
if row != nil {
jsonData, _ := json.Marshal(row)
fmt.Println("total number of rows:", len(row))
fmt.Println("row map obj:", row)
fmt.Println("row JSON output:", string(jsonData))
} else {
fmt.Println("No rows returned")
}
}
Output:
$ go run main.go
{}
total number of rows: 2
row map obj: [rethinkdb test]
row JSON output: ["rethinkdb","test"]

golang leveldb get snapshot error

I am get leveldb's all key-val to a map[string][]byte, but it is not running as my expection.
code is as below
package main
import (
"fmt"
"strconv"
"github.com/syndtr/goleveldb/leveldb"
)
func main() {
db, err := leveldb.OpenFile("db", nil)
if err != nil {
panic(err)
}
defer db.Close()
for i := 0; i < 10; i++ {
err := db.Put([]byte("key"+strconv.Itoa(i)), []byte("value"+strconv.Itoa(i)), nil)
if err != nil {
panic(err)
}
}
snap, err := db.GetSnapshot()
if err != nil {
panic(err)
}
if snap == nil {
panic("snap shot is nil")
}
data := make(map[string][]byte)
iter := snap.NewIterator(nil, nil)
for iter.Next() {
Key := iter.Key()
Value := iter.Value()
data[string(Key)] = Value
}
iter.Release()
if iter.Error() != nil {
panic(iter.Error())
}
for k, v := range data {
fmt.Println(string(k) + ":" + string(v))
}
}
but the result is below
key3:value9
key6:value9
key7:value9
key8:value9
key1:value9
key2:value9
key4:value9
key5:value9
key9:value9
key0:value9
rather not key0:value0
Problem is with casting around types (byte[] to string, etc.).
You are trying to print string values. To avoid unnecessary casting apply the following modifications:
Change data initialization into data := make(map[string]string)
Assign values into data with `data[string(Key)] = string(Value) (by the way, don't use capitalization for variables you aren't intend to export)
Print data's values with fmt.Println(k + ":" + v))
This should produce the following result:
key0:value0
key1:value1
key7:value7
key2:value2
key3:value3
key4:value4
key5:value5
key6:value6
key8:value8
key9:value9

flag redefined - panic searching by key in YouTube data API 3

I am trying to search YouTube video by key like in the golang example. I modified that code a little to let it search by different keys several times.
When I search once it is ok.
func main() {
result1, err1 := SearchYoutubeByKey("hello")
if err1 != nil {
panic(err1)
}
fmt.Println(result1)
// result2, err2 := SearchYoutubeByKey("world")
// if err2 != nil {
// panic(err2)
// }
// fmt.Println(result2)
}
But if I search twice ...
func main() {
result1, err1 := SearchYoutubeByKey("hello")
if err1 != nil {
panic(err1)
}
fmt.Println(result1)
result2, err2 := SearchYoutubeByKey("world")
if err2 != nil {
panic(err2)
}
fmt.Println(result2)
}
... then it panics with error message ...
flag redefined: query
... on line ...
query := flag.String("query", str, "Search term")
Full code:
package main
import (
"code.google.com/p/google-api-go-client/googleapi/transport"
"code.google.com/p/google-api-go-client/youtube/v3"
"flag"
"fmt"
"net/http"
)
var (
maxResults = flag.Int64("max-results", 25, "Max YouTube results")
service *youtube.Service
response *youtube.SearchListResponse
)
const developerKey = "youtube developer key"
type YoutubeSearchResult struct {
Title, YoutubeId string
}
func SearchYoutubeByKey(str string) (result []*YoutubeSearchResult, err error) {
query := flag.String("query", str, "Search term")
flag.Parse()
client := &http.Client{
Transport: &transport.APIKey{Key: developerKey},
}
service, err = youtube.New(client)
if err != nil {
return
}
// Make the API call to YouTube.
call := service.Search.List("id,snippet").
Q(*query).
MaxResults(*maxResults)
response, err = call.Do()
if err != nil {
return
}
// Iterate through each item and add it to the correct list.
for _, item := range response.Items {
switch item.Id.Kind {
case "youtube#video":
result = append(result, &YoutubeSearchResult{Title: item.Snippet.Title, YoutubeId: item.Id.VideoId})
}
}
return
}
func main() {
result1, err1 := SearchYoutubeByKey("hello")
if err1 != nil {
panic(err1)
}
fmt.Println(result1)
result2, err2 := SearchYoutubeByKey("world")
if err2 != nil {
panic(err2)
}
fmt.Println(result2)
}
So it is impossible to use this code on a website. Only the first user will be able to search first time, the others will fail.
I cannot change flag during runtime but how to search by 2 different keys in one program?
Update
working solution:
package main
import (
"code.google.com/p/google-api-go-client/googleapi/transport"
"code.google.com/p/google-api-go-client/youtube/v3"
"flag"
"fmt"
"net/http"
)
var (
maxResults = flag.Int64("max-results", 25, "Max YouTube results")
service *youtube.Service
response *youtube.SearchListResponse
query = flag.String("query", "str", "Search term")
)
const developerKey = "youtube api key"
type YoutubeSearchResult struct {
Title, YoutubeId string
}
func SearchYoutubeByKey(str string) (result []*YoutubeSearchResult, err error) {
flag.Parse()
client := &http.Client{
Transport: &transport.APIKey{Key: developerKey},
}
service, err = youtube.New(client)
if err != nil {
return
}
// Make the API call to YouTube.
call := service.Search.List("id,snippet").
Q(str).
MaxResults(*maxResults)
response, err = call.Do()
if err != nil {
return
}
// Iterate through each item and add it to the correct list.
for _, item := range response.Items {
switch item.Id.Kind {
case "youtube#video":
result = append(result, &YoutubeSearchResult{Title: item.Snippet.Title, YoutubeId: item.Id.VideoId})
}
}
return
}
func main() {
result1, err1 := SearchYoutubeByKey("hello")
if err1 != nil {
panic(err1)
}
fmt.Println(result1)
result2, err2 := SearchYoutubeByKey("world")
if err2 != nil {
panic(err2)
}
fmt.Println(result2)
}
The panic message tells you exactly what's wrong. Your command line flags should be defined only once. If you try to redefine them during runtime, it panics.

How to retrieve address of current machine?

The following grabs the local IP addresses:
package main
import (
"fmt"
"net"
)
func main() {
a, _ := net.LookupHost("localhost")
fmt.Printf("Addresses: %#+v\n",a)
}
Is this how you would normally get the local IP address, filtering the slice manually according to need?
Here's a quick and dirty modification of a code snippet originally posted by Russ Cox to the golang-nuts google group:
package main
import (
"fmt"
"net"
"os"
)
func main() {
tt, err := net.Interfaces()
if err != nil {
panic(err)
}
for _, t := range tt {
aa, err := t.Addrs()
if err != nil {
panic(err)
}
for _, a := range aa {
ipnet, ok := a.(*net.IPNet)
if !ok {
continue
}
v4 := ipnet.IP.To4()
if v4 == nil || v4[0] == 127 { // loopback address
continue
}
fmt.Printf("%v\n", v4)
}
os.Exit(0)
}
os.Exit(1)
}
Finding the correct IP address can be a problem because a typical server and development machine may have multiple interfaces. For example $ifconfig on my Mac returns the following interfaces lo0, gif0, stf0, en0, en1, en2, bridge0, p2p0, vmnet1, vmnet8, tap0, fw0, en4
Basically, you need to know your environment.
It's not pretty, but for what it's worth, this is what I use on a production Ubuntu server. It also works on my development Mac 10.9.2, who know what it does on Windows.
package main
import (
"net"
"strings"
)
func findIPAddress() string {
if interfaces, err := net.Interfaces(); err == nil {
for _, interfac := range interfaces {
if interfac.HardwareAddr.String() != "" {
if strings.Index(interfac.Name, "en") == 0 ||
strings.Index(interfac.Name, "eth") == 0 {
if addrs, err := interfac.Addrs(); err == nil {
for _, addr := range addrs {
if addr.Network() == "ip+net" {
pr := strings.Split(addr.String(), "/")
if len(pr) == 2 && len(strings.Split(pr[0], ".")) == 4 {
return pr[0]
}
}
}
}
}
}
}
}
return ""
}
func main() {
println(findIPAddress())
}
I have one addition: The current solutions shown above are not working at least on FreeBSD 10 because the system returns the addresses as CIDR notation e.g. 192.168.1.2/32! Therefore, it is necessary to change the solution a little bit:
package main
import (
"fmt"
"net"
"os"
"strings"
)
func main() {
addrs, err := net.InterfaceAddrs()
if err != nil {
fmt.Println("Error: " + err.Error())
os.Exit(1)
}
for _, a := range addrs {
text := a.String()
if strings.Contains(text, `/`) {
text = text[:strings.Index(text, `/`)]
}
ip := net.ParseIP(text)
if !ip.IsLoopback() && !ip.IsUnspecified() {
fmt.Println(ip)
}
}
}
The part ...
if strings.Contains(text, `/`) {
text = text[:strings.Index(text, `/`)]
}
... detects if / is part of the address and delete this part!
Best regards,
Thorsten
These slight modifications worked for me:
package main
import (
"fmt"
"net"
"os"
)
func myip() {
os.Stdout.WriteString("myip:\n")
addrs, err := net.InterfaceAddrs()
if err != nil {
fmt.Errorf("error: %v\n", err.Error())
return
}
for _, a := range addrs {
ip := net.ParseIP(a.String())
fmt.Printf("addr: %v loopback=%v\n", a, ip.IsLoopback())
}
fmt.Println()
}
func myip2() {
os.Stdout.WriteString("myip2:\n")
tt, err := net.Interfaces()
if err != nil {
fmt.Errorf("error: %v\n", err.Error())
return
}
for _, t := range tt {
aa, err := t.Addrs()
if err != nil {
fmt.Errorf("error: %v\n", err.Error())
continue
}
for _, a := range aa {
ip := net.ParseIP(a.String())
fmt.Printf("%v addr: %v loopback=%v\n", t.Name, a, ip.IsLoopback())
}
}
fmt.Println()
}
func main() {
fmt.Println("myip -- begin")
myip()
myip2()
fmt.Println("myip -- end")
}

Resources