
# Data Scientists You May Know


users = [
    { "id": 0, "name": "Hero" },
    { "id": 1, "name": "Dunn" },
    { "id": 2, "name": "Sue" },
    { "id": 3, "name": "Chi" },
    { "id": 4, "name": "Thor" },
    { "id": 5, "name": "Clive" },
    { "id": 6, "name": "Hicks" },
    { "id": 7, "name": "Devin" },
    { "id": 8, "name": "Kate" },
    { "id": 9, "name": "Klein" }
    ]

print("users:")
print(users)

friendships = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (3, 4),
    (4, 5), (5, 6), (5, 7), (6, 8), (7, 8), (8, 9)]

print("\nfriendships:")
print(friendships)

# add a list of friends to each user
for user in users:
   print(user)
   print("Adding attr 'friends' to dict entry")
   user["friends"] = []
   print(user, "\n")

print("users:")
print(users)


# populate the lists using the friendships data:
for i, j in friendships:
    # this works because users[i] is the user whose id is i
    users[i]["friends"].append(users[j])      # add i as a friend of j
    users[j]["friends"].append(users[i])      # add j as a friend of i

print("users:")
print(users)



# “what’s the average number of connections?
def number_of_friends(user):
    """how many friends does _user_ have?"""
    return len(user["friends"])             # length of friend_ids list

for user in users:
   print(user, number_of_friends(user) )
   print()
print()


# ==================================================
# design a “Data Scientists You May Know” suggester
# ====================================================

def friends_of_friend_ids_bad(user):
# "foaf" is short for "friend of a friend"
    return [foaf["id"]
        for friend in user["friends"]    # for each of user's friends
        for foaf in friend["friends"]]   # get each of _their_ friends

# BAD because you include people you know DIRECTLY (and yourself)
print("FAILED attempt:")
print(friends_of_friend_ids_bad(users[3]) )
print()


# Eliminate direct links and yourself
def not_the_same(user, other_user):
    """two users are not the same if they have different ids"""
    return user["id"] != other_user["id"]

def not_friends(user, other_user):
    """other_user is not a friend if he's not in user["friends"];
       I.e.: if he's not_the_same as all the people in user["friends"]"""
    return all(not_the_same(friend, other_user) for friend in user["friends"])

def friends_of_friend_ids(user):
# "foaf" is short for "friend of a friend"
    return [foaf["id"]
        for friend in user["friends"]
        for foaf in friend["friends"]
        if not_the_same(user, foaf) 
        and not_friends(user, foaf)   ] 

print("Correct:")
print(friends_of_friend_ids(users[3]) )

# Nicer presentation:
from collections import Counter
print(Counter(friends_of_friend_ids(users[3])) )

# 3 has 2 mutual friends in common with 0
# 3 has 1 mutual friends in common with 5

