Last active
October 4, 2023 16:22
-
-
Save M4tthewDE/020c18d3e34a5b0a0bf6c4eb41d2fecd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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