Forked from sebastianewak/CodeEvalChallengeSuggestGroups
Last active
August 29, 2015 14:08
-
-
Save cs3b/d4465e2e906ad1cb2f43 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
using System; | |
using System.IO; | |
using System.Text; | |
using System.Linq; | |
using System.Collections.Generic; | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
using (StreamReader reader = File.OpenText(args[0])) | |
{ | |
UsersAndGroupsRepository usersAndGroups = new UsersAndGroupsRepository(); | |
while (!reader.EndOfStream) | |
{ | |
string line = reader.ReadLine(); | |
if (null == line) | |
{ | |
continue; | |
} | |
usersAndGroups.ParseSerializedUserFriendsGroups(line); | |
} | |
var users = usersAndGroups.GetAllUsers(); | |
var groupRecommendationService = new GroupsRecommedationService(); | |
var usersAndRecommendedGroups = new Dictionary<User, ICollection<Group>>(); | |
foreach (var user in users) | |
{ | |
var recommendedGroups = groupRecommendationService.GetRecommendedGroups(user, usersAndGroups); | |
usersAndRecommendedGroups[user] = recommendedGroups; | |
} | |
var resultSerializer = new UserWithRecommendedGroupsSerializer(); | |
var result = resultSerializer.SerializeAll(usersAndRecommendedGroups); | |
Console.Out.Write(result); | |
} | |
} | |
class UsersFactory | |
{ | |
private Dictionary<string, User> usersLookup; | |
public UsersFactory() | |
{ | |
usersLookup = new Dictionary<string, User>(); | |
} | |
public User CreateUser(string name) | |
{ | |
if (string.IsNullOrEmpty(name)) | |
{ | |
return null; | |
} | |
if (!usersLookup.ContainsKey(name) || usersLookup[name] == null) | |
{ | |
usersLookup[name] = new User(name); | |
} | |
return usersLookup[name]; | |
} | |
public List<User> CreateUsers(IEnumerable<string> names) | |
{ | |
List<User> users = new List<User>(); | |
foreach (var name in names) | |
{ | |
var user = CreateUser(name); | |
if (user != null) | |
{ | |
users.Add(user); | |
} | |
} | |
return users; | |
} | |
public ICollection<User> GetAllUsers() | |
{ | |
return usersLookup.Values; | |
} | |
} | |
class GroupsFactory | |
{ | |
private Dictionary<string, Group> groupsLookup; | |
public GroupsFactory() | |
{ | |
groupsLookup = new Dictionary<string, Group>(); | |
} | |
public Group CreateGroup(string name) | |
{ | |
if (string.IsNullOrEmpty(name)) | |
{ | |
return null; | |
} | |
if (!groupsLookup.ContainsKey(name) || groupsLookup[name] == null) | |
{ | |
groupsLookup[name] = new Group(name); | |
} | |
return groupsLookup[name]; | |
} | |
public List<Group> CreateGroups(IEnumerable<string> names) | |
{ | |
var groups = new List<Group>(); | |
foreach (var name in names) | |
{ | |
var group = CreateGroup(name); | |
if (group != null) | |
{ | |
groups.Add(group); | |
} | |
} | |
return groups; | |
} | |
public ICollection<Group> GetAllGroups() | |
{ | |
return groupsLookup.Values; | |
} | |
} | |
class User | |
{ | |
public string Name; | |
public List<User> Friends; | |
public List<Group> Groups; | |
public User(string name) | |
{ | |
this.Name = name; | |
this.Friends = new List<User>(); | |
this.Groups = new List<Group>(); | |
} | |
public override string ToString() | |
{ | |
return this.Name; | |
} | |
} | |
class Group | |
{ | |
public string Name; | |
public List<User> Members; | |
public Group(string name) | |
{ | |
this.Name = name; | |
this.Members = new List<User>(); | |
} | |
public bool IsMember(User user) | |
{ | |
return this.Members.Contains(user); | |
} | |
public override string ToString() | |
{ | |
return this.Name; | |
} | |
} | |
class UsersAndGroupsRepository | |
{ | |
private UsersFactory usersFactory; | |
private GroupsFactory groupsFactory; | |
public UsersAndGroupsRepository() | |
{ | |
usersFactory = new UsersFactory(); | |
groupsFactory = new GroupsFactory(); | |
} | |
public User ParseSerializedUserFriendsGroups(string serializedUserFriendsGroup) | |
{ | |
string[] splitData = serializedUserFriendsGroup.Split(':'); | |
if (splitData == null || splitData.Length == 0) | |
{ | |
return null; | |
} | |
var userName = splitData[0]; | |
var hasFriends = splitData.Length >= 1 && splitData[1] != null; | |
var serializedFriendsNames = hasFriends ? splitData[1] : string.Empty; | |
var friendsNames = serializedFriendsNames.Split(','); | |
var hasGroups = splitData.Length >= 2 && splitData[2] != null; | |
var serializedGroupNames = hasGroups ? splitData[2] : string.Empty; | |
var groupNames = serializedGroupNames.Split(','); | |
var user = usersFactory.CreateUser(userName); | |
var friends = usersFactory.CreateUsers(friendsNames); | |
user.Friends.AddRange(friends); | |
var groups = groupsFactory.CreateGroups(groupNames); | |
user.Groups.AddRange(groups); | |
foreach (var group in groups) | |
{ | |
group.Members.Add(user); | |
} | |
return user; | |
} | |
public ICollection<User> GetAllUsers() | |
{ | |
return usersFactory.GetAllUsers(); | |
} | |
public ICollection<Group> GetAllGroups() | |
{ | |
return groupsFactory.GetAllGroups(); | |
} | |
} | |
class GroupsRecommedationService | |
{ | |
// recommend groups where >= 50% of user's friends participate | |
// assumption - don't recommend groups that user already attends | |
public ICollection<Group> GetRecommendedGroups(User user, UsersAndGroupsRepository usersAndGroupsRepository) | |
{ | |
var recommendedGroups = new List<Group>(); | |
if (user == null) | |
{ | |
return recommendedGroups; | |
} | |
var halfFriendsCount = 0.5f * user.Friends.Count; | |
var requiredNumberOfFriends = (int)Math.Ceiling(halfFriendsCount); | |
var groups = usersAndGroupsRepository.GetAllGroups().ToList<Group>(); | |
foreach (var group in user.Groups) | |
{ | |
if (groups.Contains(group)) | |
{ | |
groups.Remove(group); | |
} | |
} | |
foreach (var group in groups) | |
{ | |
if (IsRecommended(group, user.Friends, requiredNumberOfFriends)) | |
{ | |
recommendedGroups.Add(group); | |
} | |
} | |
return recommendedGroups; | |
} | |
private bool IsRecommended(Group group, IEnumerable<User> friends, int requiredNumberOfFriends) | |
{ | |
var friendsInGroupCount = 0; | |
foreach (var friend in friends) | |
{ | |
if (group.IsMember(friend)) | |
{ | |
friendsInGroupCount += 1; | |
} | |
} | |
return friendsInGroupCount >= requiredNumberOfFriends; | |
} | |
} | |
class UserWithRecommendedGroupsSerializer | |
{ | |
public string SerializeAll(Dictionary<User, ICollection<Group>> usersAndRecommendedGroups) | |
{ | |
StringBuilder stringBuilder = new StringBuilder(); | |
var users = usersAndRecommendedGroups.Keys.OrderBy(u => u.Name); | |
foreach (var user in users) | |
{ | |
var serializedLine = Serialize(user, usersAndRecommendedGroups[user]); | |
stringBuilder.Append(serializedLine); | |
} | |
return stringBuilder.ToString(); | |
} | |
private string Serialize(User user, ICollection<Group> recommendedGroups) | |
{ | |
if (user == null || recommendedGroups == null || recommendedGroups.Count == 0) | |
{ | |
return string.Empty; | |
} | |
StringBuilder stringBuilder = new StringBuilder(); | |
stringBuilder.Append(user.Name); | |
stringBuilder.Append(":"); | |
var isFirstGroup = true; | |
foreach (var group in recommendedGroups) | |
{ | |
if (!isFirstGroup) | |
{ | |
stringBuilder.Append(","); | |
} | |
else | |
{ | |
isFirstGroup = false; | |
} | |
stringBuilder.Append(group.Name); | |
} | |
stringBuilder.Append("\n"); | |
return stringBuilder.ToString(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment