Skip to content

Instantly share code, notes, and snippets.

@M4tthewDE
Last active October 4, 2023 16:22
Show Gist options
  • Save M4tthewDE/020c18d3e34a5b0a0bf6c4eb41d2fecd to your computer and use it in GitHub Desktop.
Save M4tthewDE/020c18d3e34a5b0a0bf6c4eb41d2fecd to your computer and use it in GitHub Desktop.
package helper
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"golang.org/x/exp/slices"
)
func GetContinue() bool {
var cont string
for {
fmt.Println("Do you want to continue (yes/no)? ")
fmt.Scanln(&cont)
if strings.ToLower(cont) == "no" {
return false
} else if strings.ToLower(cont) == "yes" {
return true
}
}
}
// not clear that this gets the User from stdin, maybe call it something like "ReadInUser"
// the usercap you pass in is not actually the cap, since you use "userCap-1" everywhere
// expect that value instead and adjust the callers
func GetUser(userCap int) int {
var user string
for {
fmt.Printf("\nEnter an integer in the range 0 to %d:", userCap-1)
fmt.Scanln(&user)
userNum, err := strconv.Atoi(user)
if err != nil {
fmt.Println("Error: input must be an int between 0 and 9")
continue
}
if userNum > userCap-1 || userNum < 0 {
fmt.Println("Error: input must be an int between 0 and 9")
continue
}
return userNum
}
}
// this can probably be done in the one place it's called in
// can be abstracted into a util if needed more often (>3 times)
func CreateMatrix(rows int) [][]int {
var matrix [][]int
for i := 0; i < rows; i++ {
matrix = append(matrix, []int{})
}
return matrix
}
func OpenFile() *os.File {
for {
var fileName string
fmt.Println("Enter a filename: ")
fmt.Scanln(&fileName)
fp, err := os.Open(fileName)
if err != nil {
fmt.Println("\nError in filename.")
} else {
return fp
}
}
// remove for production 🤓
//for testing
// fp, _ := os.Open("small_network_data.txt")
//return fp
}
// include in name that a network is returned
func ReadFile(fp *os.File) [][]int {
scanner := bufio.NewScanner(fp)
//Get header (total number of users) and convert to int
scanner.Scan()
userLine := scanner.Text()
numUsers, _ := strconv.Atoi(userLine)
var network [][]int = CreateMatrix(numUsers)
for scanner.Scan() {
friends := strings.Fields(scanner.Text())
// handle error
f1, _ := strconv.Atoi(friends[0])
f2, _ := strconv.Atoi(friends[1])
network[f1] = append(network[f1], f2)
network[f2] = append(network[f2], f1)
}
return network
}
// function should hide complexity, name could be "CalculateSimilarityScore"
// parameters could be "user1" and "user2"
func NumInCommonBetweenLists(user []int, other []int) int {
var inCommon int = 0
// use range to loop over user instead, much simpler 🤓
for i := 0; i < len(user); i++ {
if slices.Contains(other, user[i]) {
inCommon += 1
}
}
return inCommon
}
func CalcSimilarityScores(network [][]int) [][]int {
var simScores [][]int = CreateMatrix(len(network))
// can use "for range"
for i := 0; i < len(network); i++ {
for j := 0; j < len(network); j++ {
var inCommon int = NumInCommonBetweenLists(network[i], network[j])
simScores[i] = append(simScores[i], inCommon)
}
}
return simScores
}
// pass in userFriends instead of network, matrix is implied by the type, can be more descriptive than "simMatrix"
func Recommend(user int, network [][]int, simMatrix [][]int) int {
var suggestedUser int
var mostCompatible int = 0
uFriends := network[user]
// uMatrix very non-descriptive
uMatrix := simMatrix[user]
for i := 0; i < len(simMatrix); i++ {
if i == user {
continue
}
if slices.Contains(uFriends, i) {
continue
}
currentCompatible := uMatrix[i]
if currentCompatible > mostCompatible {
suggestedUser = i
mostCompatible = currentCompatible
}
}
return suggestedUser
}
package main
import (
"fmt"
"p5/helper"
)
func main() {
// confusing empty line, did you format with gofmt?
fmt.Println("Facebook friend recommendation.")
// why is this not done inside of helper.ReadFile()
// We don't need access to the file here
fp := helper.OpenFile()
defer fp.Close()
var network [][]int = helper.ReadFile(fp)
for {
var user int = helper.GetUser(len(network))
// clear that it's a matrix from the return type
// maybe call it something more descriptive like "similiarityScores"
simMatrix := helper.CalcSimilarityScores(network)
suggestedUser := helper.Recommend(user, network, simMatrix)
fmt.Printf("\nThe suggested friend for %d is %d\n", user, suggestedUser)
if !helper.GetContinue() {
break
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment