Insert objects in specific order with map

Issue

I have a use case where the order of objects needs to be in a specific order. The current implementation is done with using map and I’ve found numerous posts and articles where it states that map are an unordered list. All of the solutions that I found are those where they’ve made the keys as integers and they’ve used sort.Ints(keys) to sort by keys.

In the code, I’m using a yaml template to instantiate a dictionary pair, then passing it into the ProcessFruits function where it does the logic.

How would I go about getting the desired result (see below) where the object from the top of the list in fruits.yml.tmpl will always be first?

Here’s a simplified version of my code:

//Filename: fruits.yml.tmpl

fruits: {{ $fruits := processFruits
  "oranges"    true
  "bananas"    false
  "apples"    true
  }}
  {{ $fruits }}
//Filename: fruits.go

func ProcessFruits(fruits map[string]interface{}) (interface{}) {
  keys := make([]string, len(fruits))

  i := 0
  for fruit := range fruits {
    keys[i] = fruit
    i++
  }

  sort.Strings(keys)
  fmt.Println(keys)
}
// Connect fruits.yml.tmpl to the ProcessFruits function
tmpl, err := template.New(t).Funcs(template.FuncMap(map[string]interface{}{
    "processFruits":        ProcessFruits,
})).Funcs(sprig.TxtFuncMap())

Actual Results:

[apples:true bananas:false oranges:true]

Desired Results:

[oranges:true bananas:false apples:true]

Go Playground

https://go.dev/play/p/hK2AdRVsZXJ

Solution

Not the prettiest solution, but I think I’ve figured out a working code to my problem. What I’ve done was creating another dictionary that will keep track of the order of the "fruits", then combining the two dictionary together with a nested for loop and output the result to a slice.

Here’s my code:

package main

import (
    "fmt"
    "sort"
)

func ProcessFruits(fruits map[string]interface{}, counts map[int]string) {
    keys := make([]string, len(fruits))
    var foo []string
    var baz []int

    for k := range fruits {
        foo = append(foo, k)
    }
    for _, k := range foo {
        fmt.Println("Key-string:", k, "Value-bool:", fruits[k])
    }

    fmt.Println("==========================")

    // Iterate over counts (keys are ints)
    for l := range counts {
        baz = append(baz, l)
    }
    sort.Ints(baz)
    for _, l := range baz {
        fmt.Println("Key-int:", l, "Value-string:", counts[l])
    }

    fmt.Println("==========================")
    // Correlate list with sorted integer keys with the other list that contains true/false

    i := 0
    for _, m := range baz {
        for _, n := range foo {
            //fmt.Println("Key-int:", m, "Value-string:", counts[m])
            //fmt.Println("Key-string:", n, "Value-bool:", fruits[n])

            if counts[m] == n {
                keys[i] = n
                i++
                //fmt.Println(i)
            }

        }
    }

    // Desired results is now in the slice, keys.
    fmt.Println(keys)

}

func main() {

    var m = map[string]interface{}{
        "oranges": true,
        "bananas": false,
        "apples":  true,
    }

    var n = map[int]string{
        0: "oranges",
        1: "bananas",
        2: "apples",
    }

    ProcessFruits(m, n)

}

If anyone has a better solution, then I’d be curious to know.

Answered By – the1

Answer Checked By – Clifford M. (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.