Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution of the challenge completed and tested. #35

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module degrees

go 1.19
Empty file added go.sum
Empty file.
101 changes: 101 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package main

import (
_ "embed"
"log"
)

// In this solution I have used maps extensively because of the fast lookup time than slices.
// maps makes following queries vert fast :
// 1. Is Actor A in cast of movie B ?
// 2. Is Movie M contains Actor A as cast ?
// 3. Wether Actor A and Actor B are in the cast of movie M ?

// I have also created a custom data type PairSet which is struct with only one field that is a map[string]string.
// Set is implemented through maps due to followign reason:
// 1. Fast lookup
// 2. Automatic duplicate removal by Golang.

// My Approach :
// 1. Make a graph using maps where keys are actor names and values are themselves a map of key actor names and value movie.
// example : {"Actor A" : map[string]string{"Actor B" : "Movie M"}} --> This represent Actor A is related to Actor B using through M.
// 2. Find the shortest path between two nodes using BFS.

//go:embed urls.json
var bytes []byte

const URL_FILE = "" // Just for testing using go run *.go command

// BFS: Performs the Breadth-first-search on the graph to find the shortest path between two
// actors using common movie as an edge.
func BFS(graph map[string]*PairSet, source, dest string) []MovieNameCastName {
// Temporary struct just to represent a parent in the graph
type parent struct {
parentActor string
commonMovie string
}
queue := make([]string, 0)
visited := make(map[string]bool)
parents := make(map[string]parent)

queue = append(queue, source)
parents[source] = parent{parentActor: source, commonMovie: ""}

var i int
for i < len(queue) {
elem := queue[i]
visited[elem] = true
for _, v := range graph[elem].GetAllKeys() {
ok := visited[v]
if !ok {
queue = append(queue, v)
visited[v] = true
parents[v] = parent{parentActor: elem, commonMovie: graph[elem].Get(v)}
}
}
i++
}
temp := dest
var result []MovieNameCastName
for temp != source {
p := parents[temp]
result = append(result, MovieNameCastName{Cast1: p.parentActor, Cast2: temp, Movie: p.commonMovie})
temp = p.parentActor
}
return result
}

func main() {
nodes := GetCmdLineArgs()
source := nodes[0]
destination := nodes[1]

urls, err := ParseURLsFile(URL_FILE, bytes)
if err != nil {
log.Fatal(err)
}

actors, err := FetchActorsData(urls.Actor)
if err != nil {
log.Fatal(err)
}

movies, err := FetchMoviesData(urls.Movies)
if err != nil {
log.Fatal(err)
}

// {movie : {actor:role, actor:role, actor:role}, movie : {actor:role, actor:role, actor:role}}
movieMap := MakeMovieMap(actors, movies)

// {actor : {movie:role, movie:role, movie:role}, actor : {movie:role, movie:role, movie:role}}
actorMap := MakeActorMap(actors, movies)

// Adjacency List
// {actor : {actor:movie, actor:movie, actor:movie}, actor : {actor:movie, actor:movie, actor:movie}}
graph := MakeAdjecencyList(movieMap, actorMap)

shortestPath := BFS(graph, source, destination)
shortestPath = Reverse(shortestPath)
PrintResult(shortestPath, actorMap, movieMap, source, destination)
}
47 changes: 47 additions & 0 deletions models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package main

// Struct for parsing urls
type URL struct {
Movies []string `json:"movies"`
Actor []string `json:"actors"`
}

// Structs for Data handling from the URL
type CommonData struct {
URL string `json:"url"`
Name string `json:"name"`
Type string `json:"type"`
}

type Data struct {
URL string `json:"url"`
Name string `json:"name"`
Role string `json:"role"`
}

type ActorData struct {
CommonData
Movies []Data `json:"movies"`
}

type MoiveData struct {
CommonData
Casts []Data `json:"cast"`
}

// Structs below are used for internal data handling and transaformation
type RoleInMovie struct {
MovieName string `json:"name"`
Role string `json:"role"`
}

type CastInMovie struct {
CastName string `json:"name"`
Role string `json:"role"`
}

type MovieNameCastName struct {
Movie string `json:"movie"`
Cast1 string `json:"cast1"`
Cast2 string `json:"cast2"`
}
36 changes: 36 additions & 0 deletions set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

type PairSet struct {
m map[string]string
}

func New() *PairSet {
r := &PairSet{}
r.m = make(map[string]string)
return r
}

func (st *PairSet) Insert(key string, value string) {
st.m[key] = value
}

func (st *PairSet) IsPresent(key string) bool {
_, ok := st.m[key]
return ok
}

func (st *PairSet) Get(key string) string {
return st.m[key]
}

func (st *PairSet) Delete(key string) {
delete(st.m, key)
}

func (st *PairSet) GetAllKeys() []string {
var result []string
for k := range st.m {
result = append(result, k)
}
return result
}
12 changes: 12 additions & 0 deletions urls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"movies" : [
"http://data.moviebuff.com/the-great-gatsby",
"http://data.moviebuff.com/the-wolf-of-wall-street",
"http://data.moviebuff.com/taxi-driver"
],
"actors" : [
"http://data.moviebuff.com/amitabh-bachchan",
"http://data.moviebuff.com/leonardo-dicaprio",
"http://data.moviebuff.com/martin-scorsese"
]
}
Loading