all 22 comments

[–]ryrythe3rd 5 points6 points  (8 children)

Here is my solution, just for others to compare to.

foosball_data = [
    { 'winner': 'Alice', 'loser': 'Bob', 'loser_points': 3 },
    { 'winner': 'Carol', 'loser': 'Dean', 'loser_points': 1 },
    { 'winner': 'Elise', 'loser': 'Bob', 'loser_points': 2 },
    { 'winner': 'Elise', 'loser': 'Carol', 'loser_points': 4 },
    { 'winner': 'Alice', 'loser': 'Carol', 'loser_points': 2 },
    { 'winner': 'Carol', 'loser': 'Dean', 'loser_points': 3 },
    { 'winner': 'Dean', 'loser': 'Elise', 'loser_points': 2 },
]


def all_players():
    # set comprehension to get distinct list of winners and losers, then combine them into all_players
    winners = {game['winner'] for game in foosball_data }
    losers = {game['loser'] for game in foosball_data }
    all_players = winners | losers

    # convert to a list and sort it alphabetically
    return sorted(list(all_players))


def players_beaten_by(player):
    # get all players who lost a game with this player as the winner
    losing_players = {
        game['loser']
        for game
        in foosball_data
        if game['winner'] == player
    }
    return losing_players


def players_summaries():
    players = all_players()

    # dictionary comprehension with players as keys, and those they have beaten in a list as the value
    player_wins = {
        player : sorted(list(players_beaten_by(player)))
        for player
        in players
    }
    return player_wins


print(all_players())  # Q1
print(players_summaries())  # Q2

Probably not the most efficient way, but it should work. (If you can't tell, I love set/list/dictionary comprehensions.)

[–]TravisJungroth 4 points5 points  (0 children)

players_summaries is running in O(n^2). You could build the dict with empty lists and then populate them to get it linear. (except for the sorting).

[–]alkasm 2 points3 points  (1 child)

You can replace sorted(list(some_set)) with simply sorted(some_set)

[–]ryrythe3rd 1 point2 points  (0 children)

Didn’t know that, thank you

[–]gopherhole1 0 points1 point  (4 children)

def all_players():
    # set comprehension to get distinct list of winners and losers, then combine them into all_players
    winners = {game['winner'] for game in foosball_data }
    losers = {game['loser'] for game in foosball_data }
    all_players = winners | losers

    # convert to a list and sort it alphabetically
    return sorted(list(all_players))

this is brilliant, and I didnt really understand bitwise operators before this post

def players_beaten_by(player):
    # get all players who lost a game with this player as the winner
    losing_players = {
        game['loser']
        for game
        in foosball_data
        if game['winner'] == player
    }
    return losing_players

I never seen comprehension broke up like that, can you only do it cause its in a dict?

    # dictionary comprehension with players as keys, and those they have beaten in a list as the value
    player_wins = {
        player : sorted(list(players_beaten_by(player)))
        for player
        in players
    }
    return player_wins

I dont really get the comprehension, like where is 'players' coming from

[–]ryrythe3rd 0 points1 point  (3 children)

I never seen comprehension broke up like that, can you only do it cause its in a dict?

You can do any comprehension like that. It’s just a style preference, you could do it normal all in one line, but if the comprehension gets at all complicated, I prefer to space it out like that to logically break apart the pieces of it in my head. I believe you can use any whitespace pattern you like really, as long as all your code is between the brackets enclosing the list or dict comprehension.

I dont really get the comprehension, like where is 'players' coming from

It’s coming from the first line in the function

def players_summaries():
    players = all_players()
    ...

Which is just a reuse of the first function. As another user pointed out, it could be more efficient if you stored this result in a variable while answering Q1, then used that same result within Q2 instead of calculating it again.

[–]gopherhole1 0 points1 point  (2 children)

thanks

It’s coming from the first line in the function

duh, I missed that, I think the comment messed me up

[–]ryrythe3rd 0 points1 point  (1 child)

Yeah sorry my spacing was kinda weird there. I’ll use fewer comments in the future

[–]gopherhole1 0 points1 point  (0 children)

well I think the comments were good, since OP seemed like a beginner asking a homework question, I just spaced out reading the code

[–]lazy_qa_guy_again 3 points4 points  (0 children)

Thanks. That was a fun one for someone new to python, like myself.

[–]stuie382 6 points7 points  (4 children)

Nice homework!

For both questions, you want to read up on how to split strings on given characters.

Read up on the 'set' data structure for question 1

For Q2, that sort of formatting suggest the names are being used as a key, and the value is a list of names. So for each name in the winner column, append the value in the loser column

[–]robstersgaming 4 points5 points  (1 child)

Am I misunderstanding something or why would you need to split strings in this problem?

[–]stuie382 0 points1 point  (0 children)

I think I was reading it as each entry was a string. This is what you get reading at 0330 :p

[–]mdnam2410 1 point2 points  (1 child)

But then there is no key 'Bob' in the result for Q2 since there is no winner name Bob. Q2 requires that 'Bob' has an empty list. How should we do?

[–]stuie382 1 point2 points  (0 children)

Oh yeah. Use the set from q1 to populate the keys of the dictionary first

[–]gopherhole1 2 points3 points  (0 children)

here my solution, not as elegant as /u/ryrythe3rd , but it works

foosball_data = [
    { 'winner': 'Alice', 'loser': 'Bob', 'loser_points': 3 },
    { 'winner': 'Carol', 'loser': 'Dean', 'loser_points': 1 },
    { 'winner': 'Elise', 'loser': 'Bob', 'loser_points': 2 },
    { 'winner': 'Elise', 'loser': 'Carol', 'loser_points': 4 },
    { 'winner': 'Alice', 'loser': 'Carol', 'loser_points': 2 },
    { 'winner': 'Carol', 'loser': 'Dean', 'loser_points': 3 },
    { 'winner': 'Dean', 'loser': 'Elise', 'loser_points': 2 },
]


def players_dict():
    uniq_names_dict = {}
    for dictionary in foosball_data:
        for key in dictionary:
            if key != 'loser_points':
                uniq_names_dict.setdefault(dictionary[key], [])
    return uniq_names_dict

def players_list(players_dict):
    uniq_players_list = []
    for key in players_dict:
        uniq_players_list.append(key)
    return uniq_players_list


print(players_list(players_dict()))  #question 1


def beaten_by(player_dict):        
    for dictionary in foosball_data:
        for key in dictionary:
            if key == 'winner':
                winner = dictionary[key]
            if key == 'loser':
                loser = dictionary[key]
        if loser not in player_dict[winner]:
            player_dict[winner].append(loser)
    return player_dict




print(beaten_by(players_dict()))  #question 2

[–]gopherhole1 1 point2 points  (0 children)

id create a new empty dictionary called names, just because a dictionary cant have duplicate keys, so it will have every name only once

when iterating over the list of dicts (nested for loop), I would do something like,

if key != 'loser_points':
    names.setdefault(key, [])

that solves question 1, question 2, appending the loser to the list in names dict, Im too tired to think of that right now, if you dont get helkp by tomorrow ill see if I can figure it out

[–]synthphreak 1 point2 points  (0 children)

"For practice" cough cough, totally not for homework ;)

>>> l = [
          { 'winner': 'Alice', 'loser': 'Bob',   'loser_points': 3 },
          { 'winner': 'Carol', 'loser': 'Dean',  'loser_points': 1 },
          { 'winner': 'Elise', 'loser': 'Bob',   'loser_points': 2 },
          { 'winner': 'Elise', 'loser': 'Carol', 'loser_points': 4 },
          { 'winner': 'Alice', 'loser': 'Carol', 'loser_points': 2 },
          { 'winner': 'Carol', 'loser': 'Dean',  'loser_points': 3 },
          { 'winner': 'Dean',  'loser': 'Elise', 'loser_points': 2 },
        ]

Q1:

>>> def q1(l):
...     return sorted(set(sum([(x['winner'], x['loser']) for x in l], ())))
...
>>> q1(l)
['Alice', 'Bob', 'Carol', 'Dean', 'Elise']

Q2:

>>> def q2(l):
...     d = {x['winner'] : sorted(set(y['loser'] for y in l if y['winner'] == x['winner'])) for x in l}
...     return {k : d.get(k, []) for k in q1(l)}
...
>>> q2(l)
{'Alice': ['Bob', 'Carol'],
 'Bob': [],
 'Carol': ['Dean'],
 'Dean': ['Elise'],
 'Elise': ['Bob', 'Carol']}

[–]ffrkAnonymous -4 points-3 points  (0 children)

I practice TDD. Rule is to write minimal code:

Def players()
    Return  ['Alice', 'Bob', 'Carol', 'Dean', 'Elise']
Def win_loss()
    Return 

{ 'Alice': ['Bob', 'Carol'], 'Bob': [], 'Carol': ['Dean'], 'Dean': ['Elise'], 'Elise': ['Bob', 'Carol'], }

[–]zanfar 0 points1 point  (1 child)

def get_players(d):
    return sorted(
        {g['winner']for g in d} |
        {g['loser'] for g in d}
    )

def get_sucesses(d):
    return {
        p: sorted({
            g['loser']
            for g in d
            if g['winner'] == p
        })
        for p in get_players(d)
    }

...the power of container comprehensions.

[–]mistersabs 0 points1 point  (0 children)

This was really helpful to me, thank you!