像PHP风格一样的Go Array

Why?

The first programming language I’ve learnt is PHP and I love it today though I don’t write it so frequently.

The most charming part of PHP is the design of array. It combines the concept of dict list in Python, someone would say it’s not so clear but a man who has writen PHP for a long time would say that’s a fucking good design.

Recently I’ve fallen in love with Golang because of its simplicity and readability. But there would be some awful times when it comes to retriet a value from a map. This project is designed for this.

Usage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package main

import (
 "encoding/json"
 "fmt"

 array "github.com/lovelock/garray"
)

func main() {
 data := `{
  "name": "John Doe",
  "age": 30,
  "email": "john.doe@example.com",
  "isActive": true,
  "address": {
   "street": "123 Main St",
   "city": "Anytown",
   "state": "CA",
   "postalCode": "12345"
  },
  "phoneNumbers": [
   {
    "type": "home",
    "number": "555-555-5555"
   },
   {
    "type": "work",
    "number": "555-555-5556"
   }
  ],
  "projects": [
   {
    "name": "Project Alpha",
    "status": "completed",
    "tasks": [
     {
      "name": "Task 1",
      "dueDate": "2023-10-01",
      "completed": true
     },
     {
      "name": "Task 2",
      "dueDate": "2023-10-15",
      "completed": false
     }
    ]
   },
   {
    "name": "Project Beta",
    "status": "in progress",
    "tasks": [
     {
      "name": "Task 3",
      "dueDate": "2023-11-01",
      "completed": false
     },
     {
      "name": "Task 4",
      "dueDate": "2023-12-01",
      "completed": false
     }
    ]
   }
  ],
  "preferences": {
   "contactMethod": "email",
   "newsletterSubscribed": true,
   "languages": ["English", "Spanish", "German"]
  }
 }`

 var jsonMap map[string]any
 json.Unmarshal([]byte(data), &jsonMap)
 completed, err := array.Get(jsonMap, "projects", "1", "tasks", "1", "completed")
 if err != nil {
  fmt.Println("statusOfTheSecondTaskOfTheFirstProject: ", completed)
 }
}

What problems does it solve

Imagine you’ve got a complex structure like this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{
  "name": "John Doe",
  "age": 30,
  "email": "john.doe@example.com",
  "isActive": true,
  "address": {
    "street": "123 Main St",
    "city": "Anytown",
    "state": "CA",
    "postalCode": "12345"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "555-555-5555"
    },
    {
      "type": "work",
      "number": "555-555-5556"
    }
  ],
  "projects": [
    {
      "name": "Project Alpha",
      "status": "completed",
      "tasks": [
        {
          "name": "Task 1",
          "dueDate": "2023-10-01",
          "completed": true
        },
        {
          "name": "Task 2",
          "dueDate": "2023-10-15",
          "completed": false
        }
      ]
    },
    {
      "name": "Project Beta",
      "status": "in progress",
      "tasks": [
        {
          "name": "Task 3",
          "dueDate": "2023-11-01",
          "completed": false
        },
        {
          "name": "Task 4",
          "dueDate": "2023-12-01",
          "completed": false
        }
      ]
    }
  ],
  "preferences": {
    "contactMethod": "email",
    "newsletterSubscribed": true,
    "languages": ["English", "Spanish", "German"]
  }
}

How can you check if the first projects second task is completed?

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package main

import (
 "encoding/json"
 "fmt"
 "log"
)

// 定义JSON结构体
type Address struct {
 Street     string `json:"street"`
 City       string `json:"city"`
 State      string `json:"state"`
 PostalCode string `json:"postalCode"`
}

type PhoneNumber struct {
 Type   string `json:"type"`
 Number string `json:"number"`
}

type Task struct {
 Name      string `json:"name"`
 DueDate   string `json:"dueDate"`
 Completed bool   `json:"completed"`
}

type Project struct {
 Name   string `json:"name"`
 Status string `json:"status"`
 Tasks  []Task `json:"tasks"`
}

type Preferences struct {
 ContactMethod       string   `json:"contactMethod"`
 NewsletterSubscribed bool   `json:"newsletterSubscribed"`
 Languages           []string `json:"languages"`
}

type Person struct {
 Name         string        `json:"name"`
 Age          int           `json:"age"`
 Email        string        `json:"email"`
 IsActive     bool          `json:"isActive"`
 Address      Address       `json:"address"`
 PhoneNumbers []PhoneNumber `json:"phoneNumbers"`
 Projects     []Project     `json:"projects"`
 Preferences  Preferences   `json:"preferences"`
}

func main() {
 // JSON数据
 data := `{
  "name": "John Doe",
  "age": 30,
  "email": "john.doe@example.com",
  "isActive": true,
  "address": {
   "street": "123 Main St",
   "city": "Anytown",
   "state": "CA",
   "postalCode": "12345"
  },
  "phoneNumbers": [
   {
    "type": "home",
    "number": "555-555-5555"
   },
   {
    "type": "work",
    "number": "555-555-5556"
   }
  ],
  "projects": [
   {
    "name": "Project Alpha",
    "status": "completed",
    "tasks": [
     {
      "name": "Task 1",
      "dueDate": "2023-10-01",
      "completed": true
     },
     {
      "name": "Task 2",
      "dueDate": "2023-10-15",
      "completed": false
     }
    ]
   },
   {
    "name": "Project Beta",
    "status": "in progress",
    "tasks": [
     {
      "name": "Task 3",
      "dueDate": "2023-11-01",
      "completed": false
     },
     {
      "name": "Task 4",
      "dueDate": "2023-12-01",
      "completed": false
     }
    ]
   }
  ],
  "preferences": {
   "contactMethod": "email",
   "newsletterSubscribed": true,
   "languages": ["English", "Spanish", "German"]
  }
 }`

 var person Person

 // 解析JSON数据
 err := json.Unmarshal([]byte(data), &person)
 if err != nil {
  log.Fatalf("Error parsing JSON: %v", err)
 }

 // 获取第一个项目的第二个任务是否完成
 if len(person.Projects) > 0 && len(person.Projects[0].Tasks) > 1 {
  isCompleted := person.Projects[0].Tasks[1].Completed
  fmt.Printf("The second task of the first project is completed: %v\n", isCompleted)
 } else {
  fmt.Println("The required task does not exist.")
 }
}

Too complicated? What if you do not want define the structs in advance?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import (
 "encoding/json"
 "fmt"
 "log"
)

func main() {
 // JSON数据
 data := `{
 ...
 }`

 // 定义一个用来存储JSON数据的map
 var result map[string]interface{}

 // 解析JSON数据到map
 err := json.Unmarshal([]byte(data), &result)
 if err != nil {
  log.Fatalf("Error parsing JSON: %v", err)
 }

 // 获取第一个项目的第二个任务是否完成
 projects, ok := result["projects"].([]interface{})
 if !ok || len(projects) == 0 {
  log.Fatalf("No projects found or format is incorrect")
 }

 firstProject, ok := projects[0].(map[string]interface{})
 if !ok {
  log.Fatalf("First project format is incorrect")
 }

 tasks, ok := firstProject["tasks"].([]interface{})
 if !ok || len(tasks) < 2 {
  log.Fatalf("No tasks found or insufficient tasks in the first project")
 }

 secondTask, ok := tasks[1].(map[string]interface{})
 if !ok {
  log.Fatalf("Second task format is incorrect")
 }

 completed, ok := secondTask["completed"].(bool)
 if !ok {
  log.Fatalf("Completed field is missing or not a boolean")
 }

 fmt.Printf("The second task of the first project is completed: %v\n", completed)
}

Holy shit! Too many template code. Now do you miss the PHP way?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php

$jsonStr = <<<EOF
{
  "name": "John Doe",
  "age": 30,
  "email": "john.doe@example.com",
  "isActive": true,
  "address": {
   "street": "123 Main St",
   "city": "Anytown",
   "state": "CA",
   "postalCode": "12345"
  },
  "phoneNumbers": [
   {
    "type": "home",
    "number": "555-555-5555"
   },
   {
    "type": "work",
    "number": "555-555-5556"
   }
  ],
  "projects": [
   {
    "name": "Project Alpha",
    "status": "completed",
    "tasks": [
     {
      "name": "Task 1",
      "dueDate": "2023-10-01",
      "completed": true
     },
     {
      "name": "Task 2",
      "dueDate": "2023-10-15",
      "completed": false
     }
    ]
   },
   {
    "name": "Project Beta",
    "status": "in progress",
    "tasks": [
     {
      "name": "Task 3",
      "dueDate": "2023-11-01",
      "completed": false
     },
     {
      "name": "Task 4",
      "dueDate": "2023-12-01",
      "completed": false
     }
    ]
   }
  ],
  "preferences": {
   "contactMethod": "email",
   "newsletterSubscribed": true,
   "languages": ["English", "Spanish", "German"]
  }
 }
EOF;

$json = json_decode($jsonStr, true);
$statusOfTheSecondTaskOfTheFirstProject = $json['projects'][1]['completed'] ?? false;

var_dump($statusOfTheSecondTaskOfTheFirstProject);

var_dump(isset($json['projects'][2]['completed']));

In PHP you don’t have to check the validatbility of every level and you can use isset to check only the target key.

In golang operators are not allowed to be overrided, so we can use variant variables.

Examples

See the *_test.go files.

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy