1.10 نقشه map

1.10 نقشه map

1.10.1 مقدمه #

  • نقشه ، یک نوع ساختار داده است.
  • نقشه ها جهت جمع‌آوری و نگهداری مجموعه‌ای از داده‌ها استفاده می‌گردند.
  • نقشه ، از نوع داده‌های انجمنی (هش) بصورت «کلید-مقدار» است.
  • نقشه ، مجموعه‌ای از داده‌ها بصورت جفت‌‌های مرتب‌نشده است.

1.10.2 تعریف #

یک مپ شبیه به فرمت زیر است:

map[KeyType]ValueType

کلمه کلیدی map و بعد نوع کلید و در آخر هم نوع مقدار تعریف می‌شود.

  • کلید: برای اشاره به یک مقدار ذخیره شده، نیاز به یک نام‌ داریم و این یعنی «کلید» آن مقدار.

    • مقدار کلید در یک مپ، باید یکتا باشد.
    • محدودیت: برای تعریف کلید، از انواع تایپ‌هایی که قابل مقایسه هستند، می‌توان استفاده کرد:
      • Boolean(s)
      • Number(s)
      • String(s)
      • Array(s)
      • Pointer(s)
      • Struct(s)
      • Interface(s) (تا زمانی که از مقادیر مقایسه‌پذیر استفاده می‌کند)
    • از
      • Slice(s)
      • Map(s)
      • Function(s) نمی‌توان برای تعریف «کلید» مپ استفاده کرد.
    در مپ می‌توان برای کلید از مقدار داخل یک متغیر بهره برد.
    
     1package main
     2
     3import "fmt"
     4
     5func main() {
     6	myMap := make(map[int]string)
     7	myKey := 13
     8	myMap[myKey] = "thirteen"
     9	fmt.Println(myMap)        //map[13:thirteen]
    10	fmt.Println(myMap[myKey]) //thirteen
    11}
    
  • مقدار: حاوی داده‌ای است که کلید به آن اشاره می‌کند و برخلاف کلید، هیچ محدودیت برای انتخاب «نوع» آن وجود ندارد، به‌عنوان نمونه می‌توان از یک مپ دیگر برای مقدار استفاده کرد. (مپ‌های تودرتو )

map[string]map[int]string

1.10.3 ایجاد و مقداردهی اولیه #

مقدار پیش‌فرض برای یک مپ nil می‌باشد. برای مقداردهی مپ‌ ها از روش‌های زیر استفاده می‌شود:

  • استفاده از کلمه کلیدی var
1var sampleMap = map[keyType]valueType{keyName1:value1, keyName2:value2, ...}
2var sampleMap map[keyType]valueType = map[keyType]valueType{}
  • استفاده از علامت =: short variable declaration
1sampleMap := map[keyType]valueType{keyName1:value1, keyName2:value2, ...}
  • استفاده از تابع make
1var sampleMap = make(map[keyType]valueType)
2sampleMap := make(map[keyType]valueType)

1.10.4 مپ با مقدار nil #

درصورت تعریف اولیه مپ توسط دستور var sampleMap map[keyType]valueType یک مپ با مقدار nil ساخته می‌شود که نمی‌توان بدون مقداردهی اولیه، روی آن عملیات ارسال و دریافت داده‌ انجام داد:

1   var sampleMap map[uint8]int  
2   sampleMap[13] = 9999999  
3   //panic: assignment to entry in nil map

برای مقداردهی یک مپ nil که به روش زیر ساخته اید:

1var m map[string]string

از روش‌های زیر می‌توان بهره گرفت:

1var m map[string]string = map[string]string{}
2m := make(map[string]string)
3m := map[string]string{}

1.10.5 توابع مربوط به مپ #

  • تابع (len): برای برگشت تعداد عناصر داخل مپ از len استفاده می‌شود:
 1package main
 2
 3import "fmt"
 4
 5func main() {
 6	var sampleMap = map[string]bool{}
 7	var otherMap = make(map[string]uint)
 8	var nilMap map[bool]bool
 9
10	sampleMap["condition#1"] = true
11	sampleMap["condition#2"] = false
12
13	otherMap["foo"] = 1
14
15	fmt.Println(len(sampleMap))		//2
16	fmt.Println(len(otherMap))		//1
17	fmt.Println(len(nilMap))		//0 (len nil is zero)
18}

مقدار برگشتی برای تابع len روی مپ‌ nil برابر صفر (۰) است.

1.10.6 عملیات CRUD روی مپ #

برای ایجاد مپ، اغلب از تابع make استفاده می شود:

 1package main  
 2  
 3import "fmt"  
 4  
 5func main() {  
 6   animals := make(map[int]string) // nil map of string-int pairs  
 7   animals[1] = "Gopher"  
 8   animals[2] = "owl"  
 9   animals[3] = "cheetah"  
10   animals[4] = "eagle"  
11   animals[5] = "lion"  
12  
13   fmt.Println(animals)  //map[1:Gopher 2:owl 3:cheetah 4:eagle 5:lion]
14
15}

جهت خواندن مقادیر مپ می‌توان از الگوی زیر استفاده کرد: mapName["keyName"] مثال:

 1package main  
 2  
 3import "fmt"  
 4  
 5func main() {  
 6   animals := make(map[int]string) // nil map of string-int pairs  
 7   animals[1] = "Gopher"  
 8   animals[2] = "owl"  
 9   animals[3] = "cheetah"  
10   animals[4] = "eagle"  
11   animals[5] = "lion"  
12  
13   fmt.Println(animals[2]) //owl  
14}

برای بروزرسانی مقادیر مپ، از الگوی mapName[keyName] = newValue استفاده می‌شود. مثال:

 1package main  
 2  
 3import "fmt"  
 4  
 5func main() {  
 6   animals := make(map[int]string) // nil map of string-int pairs  
 7   animals[1] = "Gopher"  
 8   animals[2] = "owl"  
 9   animals[3] = "cheetah"  
10   animals[4] = "eagle"  
11   animals[5] = "lion"  
12  
13   fmt.Println(animals[2]) //owl  
14  
15   animals[2] = "wolf"  
16  
17   fmt.Println(animals[2]) //wolf  
18}

جهت حذف مقادیر در مپ، از تابع delete متعلق به پکیج builtin استفاده می‌شود.

 1package main  
 2  
 3import "fmt"  
 4  
 5func main() {  
 6   animals := make(map[int]string) // nil map of string-int pairs  
 7   animals[1] = "Gopher"  
 8   animals[2] = "owl"  
 9   animals[3] = "cheetah"  
10   animals[4] = "eagle"  
11   animals[5] = "lion"  
12  
13   fmt.Println(animals)      //map[1:Gopher 2:owl 3:cheetah 4:eagle 5:lion]  
14   fmt.Println(len(animals)) //5  
15   delete(animals, 4)  
16  
17   fmt.Println(animals)      //map[1:Gopher 2:owl 3:cheetah 5:lion]  
18   fmt.Println(len(animals)) //4  
19}

نکته: اگر کلید مورد استفاده در فانکشن delete() پیدا نشود، هیچ اتفاقی نخواهد افتاد. علت عدم بازگشت ارور در فانکشن delete() است

1// The delete built-in function deletes the element with the specified key
2// (m[key]) from the map. If m is nil or there is no such element, delete
3// is a no-op.
4func delete(m map[Type]Type1, key Type)

1.10.7 بررسی وجود کلید #

یکی از خدماتی که مپ ارائه می‌دهد،‌ پاسخ به سوال وجود یک کلید خاص در مپ می‌باشد که به‌عنوان راهکاری برای حل مسائل از آن استفاده می‌شود. مثال زیر را ببینید:

 1package main
 2
 3import "fmt"
 4
 5func main() {
 6
 7	var personData = map[string]string{"name": "frank", "family": "colleti", "dob": "1970-05-12"}
 8
 9	name, nameExist := personData["name"]
10	family, familyExist := personData["family"]
11	dob, dobExist := personData["dob"]
12	organization, organizationExist := personData["organization"]
13
14	fmt.Println(name, nameExist)	
15			//frank true
16	fmt.Println(family, familyExist)
17			//colleti true
18	fmt.Println(dob, dobExist)
19			//1970-05-12 true
20	fmt.Println(organization, organizationExist)
21			// false
22}
  • این روش بیشتر به اسم comma, ok شناخته می‌شود و بسیاری از توابع چه در کتابخانه استاندارد و چه کتابخانه‌های عمومی در گولنگ، از این نوع نام‌گذاری برای برگشت دادن مقدار و ارور پشتیبانی می‌کنند.
  • در مثال بالا تمامی متغیرهایی که با Exist تمام می‌شوند برای برسی وجود و عدم وجود یک کلید در مپ استفاده می‌شوند، به این صورت که اگر مقدار مشخص شده در مپ وجود داشت مقدار برگشتی در این متغیرها true خواهد بود و در غیر این صورت مقدار برگشتی false.

1.10.8 مپ، یک جدول، یک منبع #

وقتی یک مپ تعریف می‌شود، اگر مپ(های) دیگری از روی آن ساخته شود، دارای یک منبع (reference type) برای ذخیره و دریافت اطلاعات خواهند بود. در مثال زیر، مپ editorMap از مپ companyProfile ایجاد و وقتی ویرایش می‌شود، مپ اصلی نیز،‌ ویرایش می‌شود.

 1package main
 2
 3import "fmt"
 4
 5func main() {
 6
 7	var companyProfile = map[string]string{
 8		"name":    "companyName",
 9		"address": "sampleAddress",
10	}
11	var editorMap = companyProfile // == editorMap := companyProfile
12
13	fmt.Println(companyProfile["name"], "\t", companyProfile["address"])
14		//companyName 	 sampleAddress
15	fmt.Println(editorMap["name"], "\t", editorMap["address"])
16		//companyName 	 sampleAddress
17
18	editorMap["name"] = "new name"
19	editorMap["address"] = "new address"
20
21	//reference map also edited when editor map edit
22	fmt.Println(companyProfile["name"], "\t", companyProfile["address"])
23		//new name 	 new address
24	fmt.Println(editorMap["name"], "\t", editorMap["address"])
25		//new name 	 new address
26}

برای اینکه بتوانید مقادیر یک مپ را درون یک مپ دیگر کپی کنید، راه حل این است داخل آن مپ پیمایش کنید و مقادیرش را در مپ جدید قرار دهید.

 1package main
 2
 3import "fmt"
 4
 5func main() {
 6	var companyProfile = map[string]string{
 7		"name":    "companyName",
 8		"address": "sampleAddress",
 9	}
10
11	var editorMap = map[string]string{}
12
13	for key, value := range companyProfile {
14		editorMap[key] = value
15	}
16	
17	fmt.Println(companyProfile["name"], "\t", companyProfile["address"])
18	fmt.Println(editorMap["name"], "\t", editorMap["address"])
19
20	editorMap["name"] = "new address"
21	editorMap["address"] = "new address"
22
23	fmt.Println(companyProfile["name"], "\t", companyProfile["address"])
24	fmt.Println(editorMap["name"], "\t", editorMap["address"])
25}

1.10.9 پیمایش روی مپ #

یکی از اصلی‌ترین اهداف ایجاد و نگهداری انواع تایپ‌های مربوط به مجموعه داده‌ها، امکان دسترسی به اجزای داده و انواع لوپ از ابزارهای آن است. با استفاده از for-range می‌توان به اجزای یک داده‌ از نوع کلید-مقدار دسترسی داشت

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	animals := make(map[int][]string) // nil map of string-int pairs
 9	animals[0] = []string{"Gopher", "running", "rodent"}
10	animals[1] = []string{"owl", "flying", "carnivorous"}
11	animals[2] = []string{"cheetah", "running", "carnivorous"}
12	animals[3] = []string{"eagle", "flying", "carnivorous"}
13	animals[4] = []string{"lion", "running", "carnivorous"}
14
15	for index, animal := range animals {
16		fmt.Printf("%v- %s is %s animal and can %s \n", index, animal[0], animal[2], animal[1])
17	}
18}

خروجی کد بالا:

 1user@system:~/go/src/temp❇  GO[1.19.3]    22:29:00 
 2→  go run main.go
 30- Gopher is rodent animal and can running 
 41- owl is carnivorous animal and can flying 
 52- cheetah is carnivorous animal and can running 
 63- eagle is carnivorous animal and can flying 
 74- lion is carnivorous animal and can running 
 8user@system:~/go/src/temp❇  GO[1.19.3]    22:29:00 
 9→  go run main.go
104- lion is carnivorous animal and can running 
110- Gopher is rodent animal and can running 
121- owl is carnivorous animal and can flying 
132- cheetah is carnivorous animal and can running 
143- eagle is carnivorous animal and can flying 
15user@system:~/go/src/temp❇  GO[1.19.3]    22:29:02 
16→  go run main.go
172- cheetah is carnivorous animal and can running 
183- eagle is carnivorous animal and can flying 
194- lion is carnivorous animal and can running 
200- Gopher is rodent animal and can running 
211- owl is carnivorous animal and can flying 

به نحوه چیدمان خروجی‌ها دقت کنید، درباره علت یکسان نبودن خروجی‌ها در اجراهای متعدد تحقیق کنید تقلب کوچیک بهتون بدم :). مپ ها unordered هستن.

1.10.10 تبدیل اطلاعات رشته − مپ − اسلایس #

نمونه کد زیر یک رشته را به مپ و یک مپ را به اسلایس تبدیل می‌کند.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func seriesStringToMap(inputs ...string) map[int]string {
 8	result := make(map[int]string)
 9	for index, input := range inputs {
10		result[index] = input
11	}
12	return result
13}
14
15func mapToSlice(inputs map[int]string) []string {
16	result := make([]string, len(inputs))
17	for index, input := range inputs {
18		result[index] = input
19	}
20	return result
21}
22
23func main() {
24	myAnimal := "Eagle Cheetah Owl Lion Gopher"
25
26	myMappedAnimal := seriesStringToMap(myAnimal)
27	fmt.Println(myMappedAnimal)
28	//map[0:Eagle Cheetah Owl Lion Gopher]
29
30	mySlicedAnimal := mapToSlice(myMappedAnimal)
31	fmt.Println(mySlicedAnimal)
32	//[Eagle Cheetah Owl Lion Gopher]
33}

1.10.11 خودآزمون #

کد زیر را بررسی کنید و خروجی(های) آن را با ذهن خود پردازش کنید. سپس صحت آن(ها) را بررسی و درباره آن تحقیق کنید:

 1package main  
 2  
 3import "fmt"  
 4  
 5func main() {  
 6   var myMap map[string]int  
 7   fmt.Println(myMap)  
 8  
 9   var otherMap = map[string]int{}  
10   fmt.Println(otherMap)  
11  
12   myMap["foo"] = 13  
13   fmt.Println(myMap)  
14  
15   otherMap["bar"] = 99  
16   fmt.Println(otherMap)  
17}