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) نمی‌توان برای تعریف «کلید» مپ استفاده کرد.
    در مپ می‌توان برای کلید از مقدار داخل یک متغیر بهره برد.
    
package main

	import "fmt"

	func main() {
		myMap := make(map[int]string)
		myKey := 13
		myMap[myKey] = "thirteen"
		fmt.Println(myMap)        //map[13:thirteen]
		fmt.Println(myMap[myKey]) //thirteen
	}
  • مقدار: حاوی داده‌ای است که کلید به آن اشاره می‌کند و برخلاف کلید، هیچ محدودیت برای انتخاب «نوع» آن وجود ندارد، به‌عنوان نمونه می‌توان از یک مپ دیگر برای مقدار استفاده کرد. (مپ‌های تودرتو )
map[string]map[int]string

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

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

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

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

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

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

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

var m map[string]string

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

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

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

  • تابع (len): برای برگشت تعداد عناصر داخل مپ از len استفاده می‌شود:
    package main
    
    import "fmt"
    
    func main() {
    	var sampleMap = map[string]bool{}
    	var otherMap = make(map[string]uint)
    	var nilMap map[bool]bool
    
    	sampleMap["condition#1"] = true
    	sampleMap["condition#2"] = false
    
    	otherMap["foo"] = 1
    
    	fmt.Println(len(sampleMap))		//2
    	fmt.Println(len(otherMap))		//1
    	fmt.Println(len(nilMap))		//0 (len nil is zero)
    }

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

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

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

package main

import "fmt"

func main() {
   animals := make(map[int]string) // nil map of string-int pairs
   animals[1] = "Gopher"
   animals[2] = "owl"
   animals[3] = "cheetah"
   animals[4] = "eagle"
   animals[5] = "lion"

   fmt.Println(animals)  //map[1:Gopher 2:owl 3:cheetah 4:eagle 5:lion]

}

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

package main

import "fmt"

func main() {
   animals := make(map[int]string) // nil map of string-int pairs
   animals[1] = "Gopher"
   animals[2] = "owl"
   animals[3] = "cheetah"
   animals[4] = "eagle"
   animals[5] = "lion"

   fmt.Println(animals[2]) //owl
}

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

package main

import "fmt"

func main() {
   animals := make(map[int]string) // nil map of string-int pairs
   animals[1] = "Gopher"
   animals[2] = "owl"
   animals[3] = "cheetah"
   animals[4] = "eagle"
   animals[5] = "lion"

   fmt.Println(animals[2]) //owl

   animals[2] = "wolf"

   fmt.Println(animals[2]) //wolf
}

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

package main

import "fmt"

func main() {
   animals := make(map[int]string) // nil map of string-int pairs
   animals[1] = "Gopher"
   animals[2] = "owl"
   animals[3] = "cheetah"
   animals[4] = "eagle"
   animals[5] = "lion"

   fmt.Println(animals)      //map[1:Gopher 2:owl 3:cheetah 4:eagle 5:lion]
   fmt.Println(len(animals)) //5
   delete(animals, 4)

   fmt.Println(animals)      //map[1:Gopher 2:owl 3:cheetah 5:lion]
   fmt.Println(len(animals)) //4
}

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

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

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

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

package main

import "fmt"

func main() {

	var personData = map[string]string{"name": "frank", "family": "colleti", "dob": "1970-05-12"}

	name, nameExist := personData["name"]
	family, familyExist := personData["family"]
	dob, dobExist := personData["dob"]
	organization, organizationExist := personData["organization"]

	fmt.Println(name, nameExist)
			//frank true
	fmt.Println(family, familyExist)
			//colleti true
	fmt.Println(dob, dobExist)
			//1970-05-12 true
	fmt.Println(organization, organizationExist)
			// false
}
  • این روش بیشتر به اسم comma, ok شناخته می‌شود و بسیاری از توابع چه در کتابخانه استاندارد و چه کتابخانه‌های عمومی در گولنگ، از این نوع نام‌گذاری برای برگشت دادن مقدار و ارور پشتیبانی می‌کنند.
  • در مثال بالا تمامی متغیرهایی که با Exist تمام می‌شوند برای برسی وجود و عدم وجود یک کلید در مپ استفاده می‌شوند، به این صورت که اگر مقدار مشخص شده در مپ وجود داشت مقدار برگشتی در این متغیرها true خواهد بود و در غیر این صورت مقدار برگشتی false.

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

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

package main

import "fmt"

func main() {

	var companyProfile = map[string]string{
		"name":    "companyName",
		"address": "sampleAddress",
	}
	var editorMap = companyProfile // == editorMap := companyProfile

	fmt.Println(companyProfile["name"], "\t", companyProfile["address"])
		//companyName 	 sampleAddress
	fmt.Println(editorMap["name"], "\t", editorMap["address"])
		//companyName 	 sampleAddress

	editorMap["name"] = "new name"
	editorMap["address"] = "new address"

	//reference map also edited when editor map edit
	fmt.Println(companyProfile["name"], "\t", companyProfile["address"])
		//new name 	 new address
	fmt.Println(editorMap["name"], "\t", editorMap["address"])
		//new name 	 new address
}

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

package main

import "fmt"

func main() {
	var companyProfile = map[string]string{
		"name":    "companyName",
		"address": "sampleAddress",
	}

	var editorMap = map[string]string{}

	for key, value := range companyProfile {
		editorMap[key] = value
	}

	fmt.Println(companyProfile["name"], "\t", companyProfile["address"])
	fmt.Println(editorMap["name"], "\t", editorMap["address"])

	editorMap["name"] = "new address"
	editorMap["address"] = "new address"

	fmt.Println(companyProfile["name"], "\t", companyProfile["address"])
	fmt.Println(editorMap["name"], "\t", editorMap["address"])
}

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

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

package main

import (
	"fmt"
)

func main() {
	animals := make(map[int][]string) // nil map of string-int pairs
	animals[0] = []string{"Gopher", "running", "rodent"}
	animals[1] = []string{"owl", "flying", "carnivorous"}
	animals[2] = []string{"cheetah", "running", "carnivorous"}
	animals[3] = []string{"eagle", "flying", "carnivorous"}
	animals[4] = []string{"lion", "running", "carnivorous"}

	for index, animal := range animals {
		fmt.Printf("%v- %s is %s animal and can %s \n", index, animal[0], animal[2], animal[1])
	}
}

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

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

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

package main

import (
	"fmt"
)

func seriesStringToMap(inputs ...string) map[int]string {
	result := make(map[int]string)
	for index, input := range inputs {
		result[index] = input
	}
	return result
}

func mapToSlice(inputs map[int]string) []string {
	result := make([]string, len(inputs))
	for index, input := range inputs {
		result[index] = input
	}
	return result
}

func main() {
	myAnimal := "Eagle Cheetah Owl Lion Gopher"

	myMappedAnimal := seriesStringToMap(myAnimal)
	fmt.Println(myMappedAnimal)
	//map[0:Eagle Cheetah Owl Lion Gopher]

	mySlicedAnimal := mapToSlice(myMappedAnimal)
	fmt.Println(mySlicedAnimal)
	//[Eagle Cheetah Owl Lion Gopher]
}

1.10.11 خودآزمون #

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

package main

import "fmt"

func main() {
   var myMap map[string]int
   fmt.Println(myMap)

   var otherMap = map[string]int{}
   fmt.Println(otherMap)

   myMap["foo"] = 13
   fmt.Println(myMap)

   otherMap["bar"] = 99
   fmt.Println(otherMap)
}