goslice

Taking a slice pointer and returning pointers to that slice's elements


Im currently learning GO and i decided to redo a character ranker i made in python a year ago as an exercise. The program takes user input and makes a slice of structs with them (the struct consists of a name inputted and a score of 0), then a function creates matches between the inputed characters so the user can choose between the two in pair. full code here


type Char struct {
    Name  string
    Score uint8
}

func main() {
    baseSlice := InputCharacters()
    matchups := MatchMaking(baseSlice)
    voting := Voting(matchups)
    n := 0
    for _, v := range voting {
        fmt.Printf("%d: %v(%v)\n", n, v.Name, v.Score)
        n++
    }
    /* the for returns 2 copies of each character,
       none of them have any points */

}

func InputCharacters() []structs.Char {

    var charSlice []structs.Char = []structs.Char{}
    var reader = bufio.NewReader(os.Stdin)

    for {

        fmt.Println("Enter a character name (or quit/q to finish):")

        name, err := reader.ReadString('\n')
        name = strings.TrimSpace(name)
        if err != nil {
            fmt.Printf("Reading error: %v", err)
        }

        if name == "quit" || name == "q" {
            return charSlice
        }

        for i := range charSlice {
            if charSlice[i].Name == name {
                charSlicePostDelete := structs.RemoveElement(charSlice, i)
                charSlice = charSlicePostDelete
                break
            }
        }

        charSlice = append(charSlice, structs.Char{Name: name, Score: 0})

    }
}

func MatchMaking(arr []structs.Char) map[[2]structs.Char]bool {
    m := make(map[[2]structs.Char]bool)
    for _, v1 := range arr {
        for _, v2 := range arr {
            pair := [2]structs.Char{v1, v2}

            if pair[0].Name == pair[1].Name {
                continue
            }

            sortedPair := [2]structs.Char{SortAlphabetically(pair[0], pair[1]), SortAlphabeticallyReverse(pair[0], pair[1])}
            if _, ok := m[sortedPair]; !ok {
                m[sortedPair] = true
            }
        }
    }
    return m
}

func Voting(pairs map[[2]structs.Char]bool) []structs.Char {

    var reader = bufio.NewReader(os.Stdin)

    for i := range pairs {
        fmt.Println("Vote for your favourite (0 for left, 1 for right)")
        fmt.Printf("%v - %v\n", i[0].Name, i[1].Name)
        name, err := reader.ReadString('\n')
        name = strings.TrimSpace(name)
        if err != nil {
            fmt.Printf("Reading error: %v", err)
        }
        nameToInt, err := strconv.ParseInt(name, 0, 8)
        if err != nil {
            fmt.Printf("Reading error: %v", err)
        }
        switch nameToInt {
        case 0:
            i[0].Score = i[0].Score + 1
        case 1:
            i[1].Score = i[1].Score + 1
        }
    }

    var charArr []structs.Char = make([]structs.Char, 0, len(pairs)*2)
    for i := range pairs {
        charArr = append(charArr, i[0], i[1])
    }
    return charArr

}


func SortAlphabetically(a, b structs.Char) structs.Char {
    if a.Name < b.Name {
        return a
    }
    return b
}

func SortAlphabeticallyReverse(a, b structs.Char) structs.Char {
    if a.Name > b.Name {
        return a
    }
    return b
}

I want the Matchmaking func to return pointers to the elements of the original []structs.Char instead of copies, so that i can then change the score property in a different function and that change will be seen in the original slice. The MatchMaking() func instead returns a different copy of the char struct each type that struct instance is needed, so if Mathmaking returns two pairs with the struct 'c', each 'c' will be a different copy of that struct. I'm a bit confused by the concept of pointers and how they work in go so im not sure how to implement this to my functions.


Solution

  • The wording is a bit fuzzy(you use 'return' a lot in contexts where I think you mean use or pass), but what I believe that you want is to change the underlying values in the Char structs. For that, we'll use pointers to Char - *Char(in this case, a map of pairs of *Char to bool) in both the MatchMaking and the Voting methods. That way any change you do propagates to the base structs, since you're now passing pointers around instead of struct copies

    func MatchMaking(arr []Char) map[[2]*Char]bool {
        m := make(map[[2]*Char]bool)
        for i := 0; i < len(arr); i++ {
            for j := i + 1; j < len(arr); j++ {
                pair := [2]*Char{&arr[i], &arr[j]}
    
                if pair[0].Name == pair[1].Name {
                    continue
                }
    
                sortedPair := [2]*Char{SortAlphabetically(pair[0], pair[1]), SortAlphabeticallyReverse(pair[0], pair[1])}
                if _, ok := m[sortedPair]; !ok {
                    m[sortedPair] = true
                }
            }
        }
        return m
    }
    
    func Voting(pairs map[[2]*Char]bool) []Char {
        var reader = bufio.NewReader(os.Stdin)
    
        for i := range pairs {
            fmt.Println("Vote for your favourite (0 for left, 1 for right)")
            fmt.Printf("%v - %v\n", (*i[0]).Name, (*i[1]).Name)
            name, err := reader.ReadString('\n')
            name = strings.TrimSpace(name)
            if err != nil {
                fmt.Printf("Reading error: %v", err)
            }
            nameToInt, err := strconv.ParseInt(name, 0, 8)
            if err != nil {
                fmt.Printf("Reading error: %v", err)
            }
            switch nameToInt {
            case 0:
                (*i[0]).Score++
            case 1:
                (*i[1]).Score++
            }
        }
    
        var charArr []Char = make([]Char, 0, len(pairs)*2)
        for i := range pairs {
            charArr = append(charArr, *i[0], *i[1])
        }
        return charArr
    }