Kolejność podczas iteracji po mapie

0

Cześć, na początek zrzut mojej mapy

map[CreateStudyPlan:map[...] GetStudyPlan:map[...]]

Iteruje po niej w ten sposób

		for key, _ := range steps.(map[interface {}]interface {})  {
			fmt.Println(key)
		}

Problem jaki napotykam jest taki, że niekiedy zmienia się kolejność wypisywanych wartości
i zamiast tego:
CreateStudyPlan
GetStudyPlan

Dostaje to:
GetStudyPlan
CreateStudyPlan

Można coś z tym zrobić, ograć to w jakiś sposób? Szukałem na stacku odpowiedzi i nic.

5

Kolejność iterowania po mapie jest nieokreślona. Rozwiązaniem jest stworzenie osobnego wycinka na klucze i posortowanie ich.

package main

import (
	"fmt"
	"sort"
)

func main() {
	ages := map[string]int{
		"foo": 31,
		"bar": 34,
	}

	names := make([]string, 0, len(ages))
	for name := range ages {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		fmt.Printf("%s\t%d\n", name, ages[name])
	}
}
3

Tak, to rozwiązanie znalazłem, problem w tym, że nie mam reguły sortujące. Mape tworze na podstawie treści pliku yml i iterowanie powinno odbywać się zgodnie z kolejnością jaka wyciągana jest z pliku.

A jak ten YML wygląda? Bo wiesz, że obiekty (mapy) w YAMLu nie mają określonej kolejności?

0

Swój interface {} możesz konwertować na string z fmt.Sprintf("%v", value). Dla takiego YAML-a:

r: 1
c: 5
b: 23
s: 2
d: 12
p: 6

Mogę rozszerzyć poprzedni przykład:

package main

import (
	"fmt"
	"io/ioutil"
	"sort"

	"gopkg.in/yaml.v3"
)

func main() {
	yfile, err := ioutil.ReadFile("test.yaml")
	if err != nil {
		panic(err)
	}

	data := make(map[interface{}]interface{})
	err2 := yaml.Unmarshal(yfile, &data)
	if err2 != nil {
		panic(err2)
	}

	keys := make([]string, 0, len(data))
	for key := range data {
		keys = append(keys, fmt.Sprintf("%v", key))
	}
	sort.Strings(keys)
	for _, key := range keys {
		fmt.Printf("%s: %d\n", key, data[key])
	}
}

Wywołanie

$ go run test.go 
b: 23
c: 5
d: 12
p: 6
r: 1
s: 2
1

Doczytałem, że OP-owi chodzi o kolejność, w jakiej ma obiekty w pliku, a nie alfabetyczną; w takim razie kolejność jest tracona w momencie wstawienia do mapy. Wobec tego zamiast do niej można by załadować Yaml do obiektu yaml.Node, który reprezentuje "surowy" i nieprzetworzony kawałek Yamla, wychodzi coś takiego:

package main

import (
	"fmt"
	"io/ioutil"

	"gopkg.in/yaml.v3"
)

func main() {
	yfile, err := ioutil.ReadFile("test.yaml")
	if err != nil {
		panic(err)
	}

	data := yaml.Node{}
	err = yaml.Unmarshal(yfile, &data)
	if err != nil {
		panic(err)
	}
	for i := 0; i < len(data.Content[0].Content); i += 2 {
		fmt.Printf("%v: %v\n", data.Content[0].Content[i].Value, data.Content[0].Content[i+1].Value)
	}
}

Efekt:

$ go run test.go 
r: 1
c: 5
b: 23
s: 2
d: 12
p: 6

Innymi słowy, problem XY.

0

Hmm właśnie rzecz w tym, że kiedy ładuje dane z pliku do mapy to kolejność zawsze jest zachowana, to wynika z logów na ekranie.
Czy to ma jakiekolwiek znaczenie? Czy pod spodem kolejność i tak może być poprzestawiana tylko tego nie widać?
Test wykonywałem kilkadziesiąt razy i zawsze kolejność byłą dobra, tylko przy iteracji się sypie ale też nieczęsto.

Rozwiązanie z yamlNode wygląda to fajnie, mógłbyś mi jeszcze podpowiedzieć jak to zrobić dla nieco bardziej złożonej struktury niż klucz : wartość?

np:

Keys:
  KeyName:
    val1: "POST"
    val2: "/api/v1/study-programs/"
    nestedVal1:
      v: "201"
    nestedVal2:
      z: "stringSample"

1 użytkowników online, w tym zalogowanych: 0, gości: 1