Getting the map value with Golang - dictionary

I couldn't do it because I didn't fully understand the logic, but you can look at my code example and output below. I just want to return the country name as a variable in the output.
Json File : https://gist.githubusercontent.com/coderantidote/0894cf7c5204d4c712207ff9162d044d/raw/ab9ec19dcfecd93addb4b1961a2506b34164c090/tld
package main
import (
"fmt"
"strings"
tld "github.com/jpillora/go-tld"
gojsonq "github.com/thedevsaddam/gojsonq/v2"
)
func main() {
urls := []string{
"http://google.com",
"https://eduuk.co.uk/",
"https://www.medi-cal.ca.gov/",
}
for _, url := range urls {
u, _ := tld.Parse(url)
jq := gojsonq.New().File("./tld.json").From("tlds")
tldSlice := strings.Split(u.TLD, ".")
if len(tldSlice) == 2 {
jq.Where("fields.tld", "=", tldSlice[1]).Select("fields.country")
} else {
jq.Where("fields.tld", "strictContains", tldSlice[0]).Select("fields.country")
}
fmt.Printf("%s\n", u.TLD)
fmt.Printf("%v\n", jq.Get())
}
}
Output:
com
[map[country:Commercial organizations]]
co.uk
[map[country:United Kingdom]]
gov
[map[country:US government]]

Is this what you are looking for?
fmt.Printf("%s\n", u.TLD)
fmt.Printf("%v\n", jq.First().(map[string]interface{})["country"])

Related

cmparing two yaml files and output the difference

I have two yaml files and i want to show the difference between the two. The yaml files are below. I want to display difference between the two yamls.
Like under deletion it should be name:"first" and under updates it should be a full path something like profiles:names:description:"second yaml".
The code below gives me the full object in case of a deeply nested object. How do i get the full path?
YAML1
`
id: 5
name: "first"
repo: "some repo"
profiles:
id: 3
name: default
description: "first yaml"
data:
logs: []
config:
hosts:
url.com: minikube
`
YAML2
`
id: 5
repo: "some repo"
profiles:
id: 3
name: default
description: "second yaml"
data:
logs: []
config:
hosts:
url.com: minikube
`
`
package main
import (
"fmt"
"io/ioutil"
"log"
"reflect"
"gopkg.in/yaml.v3"
)
type Modifications struct {
addition map[string]interface{}
deletion map[string]interface{}
update map[string]interface{}
}
func NewModifications() *Modifications {
var m Modifications
m.addition = make(map[string]interface{})
m.deletion = make(map[string]interface{})
m.update = make(map[string]interface{})
return &m
}
func compareYaml(modifications *Modifications) {
// read file
yfile1, err := ioutil.ReadFile("./old.yaml")
if err != nil {
fmt.Println(err)
log.Fatal(err)
}
yfile2, err := ioutil.ReadFile("./new.yaml")
if err != nil {
log.Fatal(err)
//return
}
// unmarshal ymal
data1 := make(map[string]interface{})
if err := yaml.Unmarshal(yfile1, &data1); err != nil {
fmt.Println(err)
log.Fatal(err)
//return
}
data2 := make(map[string]interface{})
if err := yaml.Unmarshal(yfile2, &data2); err != nil {
fmt.Println(err)
log.Fatal(err)
//return
}
// from this we can iterate the key in data2 then check whether it exists in data1
// if so then we can update the value in data2
// iterate key1 in data2
for key1, value1 := range data1 {
// check whether key1 exists in data2
if _, exists := data2[key1]; exists {
// see if it is an update
if reflect.DeepEqual(data2[key1], value1) {
delete(data2, key1)
} else {
modifications.update[key1] = data2[key1]
delete(data2, key1)
}
} else {
modifications.deletion[key1] = value1
}
}
for k, v := range data2 {
modifications.addition[k] = v
}
}
func main() {
modifications := NewModifications()
compareYaml(modifications)
fmt.Println("************")
fmt.Println("adddition")
fmt.Println(modifications.addition)
fmt.Println("************")
fmt.Println("deletion")
fmt.Println(modifications.deletion)
fmt.Println("************")
fmt.Println("update")
fmt.Println(modifications.update)
fmt.Println("************")
}
`
Playground
You may want to use the compare package:
https://github.com/google/go-cmp
There is a cmp.Diff that allows you to compare two objects and will return the diff in string format.

Trying to use channels in go but data is not properly sent/received into the channel

The hope is to quickly parse a very large number of similar URLs (only one 'id' element differs from one to the next) and dump the response body into a channel that will later be queried by the main function and used to build a text file.
Inside the getpageCanal() function, the body seems to be ok, but after that, I don't understand why the channel doesn't properly load the body string.
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main() {
initial := "https://www1.medion.de/downloads/index.pl?op=detail&id="
ending := "&type=treiber&lang=uk"
links := []string{}
os.Remove("dump.txt")
dumpFile, _ := os.Create("dump.txt")
c := make(chan string)
for i := 16000; i < 16004; i++ {
links = append(links, initial+fmt.Sprint(i)+ending)
}
fmt.Println(links[0])
for _, link := range links {
//the hope is to make this a go routine, but first I need to just make it work
getpageCanal(c, link)
}
for el := range c {
fmt.Println(el)
n, err := dumpFile.WriteString(el)
if err != nil {
fmt.Println(err)
}
if n == 0 {
fmt.Println(" nothing written in main")
}
}
}
func getpageCanal(canal chan string, url string) {
defer close(canal)
page, err := http.Get(url)
if err != nil {
fmt.Println("you done fucked up, boy")
}
content, er2 := ioutil.ReadAll(page.Body)
//fmt.Println(content)
if er2 != nil {
fmt.Println(er2)
}
canal <- string(content)
}
After modifying the code as instructed by the first comments (not closing the channel after each call and making the call to the worker function a go routine) I will now provide you with a working version:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main() {
initial := "https://www1.medion.de/downloads/index.pl?op=detail&id="
ending := "&type=treiber&lang=uk"
links := []string{}
os.Remove("dump.txt")
dumpFile, _ := os.Create("dump.txt")
c := make(chan string)
for i := 16000; i < 16004; i++ {
links = append(links, initial+fmt.Sprint(i)+ending)
}
fmt.Println(links[0])
for _, link := range links {
go getpageCanal(c, link)
}
for el := range c {
fmt.Println(el)
n, err := dumpFile.WriteString(el)
if err != nil {
fmt.Println(err)
}
if n == 0 {
fmt.Println(" nothing written in main")
}
}
}
func getpageCanal(canal chan string, url string) {
//defer close(canal)
page, err := http.Get(url)
if err != nil {
fmt.Println("you done fucked up, boy")
}
content, er2 := ioutil.ReadAll(page.Body)
//fmt.Println(content)
if er2 != nil {
fmt.Println(er2)
}
canal <- string(content)
}

Convert map[string][]string into a yaml structure

I try to convert a make(map[string]string) into a yaml like that:
Yaml Output desire:
items:
keys1:value1
keys2:value2
keys3:value3
keys4:value4
The keys,values are this listKey map of string. J = string = {"key1":"value1","key2":"value2" }
type Items struct {
items string
ItemsValues map[string][]string
}
func ConvertToYelm(j string){
y := Items{}
var dataJson map[string]string
err := json.Unmarshal([]byte(j), &dataJson)
if err != nil {
fmt.Println(err)
return
}
listKey := make(map[string]string)
for k := range dataJson{
listKey[k] = k
}
yelm, err := yaml.Marshal(listKey)
if err != nil {
fmt.Println(err)
return
}
err = yaml.Unmarshal(yelm, Items)
if err != nil {
fmt.Println(err)
return
}
yeml2, err := yaml.Marshal(&yelm)
fmt.Printf ("%s", string(yeml2))
To be honest, I'm a little bit lost here, thank you for the help
To get the exact YAML from your post:
items:
keys1:value1
keys2:value2
keys3:value3
keys4:value4
You can do this (Go Playground):
package main
import (
"fmt"
"gopkg.in/yaml.v2"
)
type ItemsStruct struct {
Items map[string]string `yaml:"items"`
}
func main() {
itms := &ItemsStruct{Items: map[string]string{
"keys1": "value1",
"keys2": "value2",
"keys3": "value3",
"keys4": "value4"}}
yamlBytes, err := yaml.Marshal(itms)
if err != nil {
//handle error
}
fmt.Println(string(yamlBytes))
}
And just to add, I see your code is decoding this JSON {"key1":"value1", "key2":"value2", ... } and then encoding it as YAML in your specified format. Here is the Go Playground for that.

Navigation into a map with a string path variable for golang

I would like to directly navigate to a value in a map. Lets be more specific with the following go code example which should give me the value of "Walter" directly: (https://play.golang.org/p/tYJsvp39hn)
type Signature struct{
Name string
Signed bool
}
path := "Document.Signatures.1.Name"
map := map[string]interface{}{
"Document": map[string]interface{}{
"Signatures": []interface{}{
Signature{ Name: "Hugo", Signed: false },
Signature{ Name: "Walter", Signed: false },
},
"Otherstuff": "asadwa",
},
"AlsoOtherStuff": "adwaw",
}
// map.giveMe(path)
// even better (if possible:) map.change(path,"ToThisNewValue")
I have searched for solutions, but I can't find any on the internet. Maybe one of you knows how to do this or knows a library to use for me.
Thank you so much in advance!
Quite a lot of reflect calls will be needed if there is no predefined struct.
That being said, you can do it by iterating through the map with type checking on every iteration and handling cases accordingly.
// Splitting the path into keys
keys := strings.Split(path, ".")
var value interface{} = map1
for _, key := range keys {
if value, err = Get(key, value); err != nil {
break
}
}
if err == nil {
fmt.Println("Value:", value)
} else {
fmt.Println("Error:", err)
}
func Get(key string, s interface{}) (v interface{}, err error) {
var (
i int64
ok bool
)
switch s.(type) {
case map[string]interface{}:
if v, ok = s.(map[string]interface{})[key]; !ok {
err = fmt.Errorf("Key not present. [Key:%s]", key)
}
case []interface{}:
if i, err = strconv.ParseInt(key, 10, 64); err == nil {
array := s.([]interface{})
if int(i) < len(array) {
v = array[i]
} else {
err = fmt.Errorf("Index out of bounds. [Index:%d] [Array:%v]", i, array)
}
}
case Signature:
r := reflect.ValueOf(s)
v = reflect.Indirect(r).FieldByName(key)
}
//fmt.Println("Value:", v, " Key:", key, "Error:", err)
return v, err
}
Playground code

Go map of functions

I have Go program that has a function defined. I also have a map that should have a key for each function. How can I do that?
I have tried this, but this doesn't work.
func a(param string) {
}
m := map[string] func {
'a_func': a,
}
for key, value := range m {
if key == 'a_func' {
value(param)
}
}
Are you trying to do something like this? I've revised the example to use varying types and numbers of function parameters.
package main
import "fmt"
func f(p string) {
fmt.Println("function f parameter:", p)
}
func g(p string, q int) {
fmt.Println("function g parameters:", p, q)
}
func main() {
m := map[string]interface{}{
"f": f,
"g": g,
}
for k, v := range m {
switch k {
case "f":
v.(func(string))("astring")
case "g":
v.(func(string, int))("astring", 42)
}
}
}
m := map[string]func(string, string)
Works if you know the signature (and all the funcs have the same signature)
I think this is cleaner/safer than using interface{}
You can define a type if functions are same interface.
package main
import "log"
type fn func (string)
func foo(msg string) {
log.Printf("foo! Message is %s", msg)
}
func bar(msg string) {
log.Printf("bar! Message is %s", msg)
}
func main() {
m := map[string] fn {
"f": foo,
"b": bar,
}
log.Printf("map is %v", m)
m["f"]("Hello")
m["b"]("World")
}
#Seth Hoenig's answer helped me best, but I just wanted to add that Go accepts functions with defined return value as well:
package main
func main() {
m := map[string]func(string) string{
"foo": func(s string) string { return s + "nurf" },
}
m["foo"]("baz") // "baznurf"
}
If you think it's ugly, you could always use a type (see #smagch's answer).
I used a map[string]func (a type, b *type) I passed a string to search the map and a pointer to modify the slice.
Hope that helps!
var Exceptions map[string]func(step string, item *structs.Item)
func SetExceptions() {
Exceptions = map[string]func(a string, i *structs.Item){
"step1": step1,
}
}
func RunExceptions(state string, item *structs.Item) {
method, methBool := Exceptions[state]
if methBool {
method(state, item)
}
}
func step1(step string, item *structs.Item) {
item.Title = "Modified"
}
Here is the way I made it work in my case:
package main
import (
"fmt"
)
var routes map[string]func() string
func main() {
routes = map[string]func() string{
"GET /": homePage,
"GET /about": aboutPage,
}
fmt.Println("GET /", pageContent("GET /"))
fmt.Println("GET /about", pageContent("GET /about"))
fmt.Println("GET /unknown", pageContent("GET /unknown"))
// Output:
// GET / Home page
// GET /about About page
// GET /unknown 404: Page Not Found
}
func pageContent(route string) string {
page, ok := routes[route]
if ok {
return page()
} else {
return notFoundPage()
}
}
func homePage() string {
return "Home page"
}
func aboutPage() string {
return "About page"
}
func notFoundPage() string {
return "404: Page Not Found"
}
https://play.golang.org/p/8_g6Di1OKZS
Hope this works for you(you can use interface{} instead any)
package main
import (
"fmt"
)
func toon(v any) {
fmt.Println(v)
}
func main() {
names := map[string]any{
"Function": toon,
}
names["Function"].(func(any))("a")
}

Resources