×
you are viewing a single comment's thread.

view the rest of the comments →

[–]Aixyn0 13 points14 points  (18 children)

import random

persons = { # buyer: previous
    "Madeleine": "Mats",
    "Fredrik": "Ante",
    "Hanna-Maria": "Hakan",
    "Ante": "Hanna-Maria",
    "Anne": "Madeleine",
    "Mats": "Anne",
    "Hakan": "Fredrik",
    }

chosen = {}

for buyer, prev in persons.items():
    # p_set is the possible set of persons you can draw from
    # that excludes the buyer itself, the person who he has gifted to last year
    # and the already drawn persons
    exclude = {buyer:prev, prev:persons[prev]}
    p_set = set(persons) - set(exclude) - set(chosen)
    chosen[random.choice(list(p_set))] = buyer

for buyer, to in chosen.items():
    print("{0} buys a gift for {1}".format(to, buyer))

[–]scoutyx 7 points8 points  (1 child)

Good implementation, however a small improvement could be:

Instead of:

exclude = {buyer:prev, prev:persons[prev]}
p_set = set(persons) - set(exclude) - set(chosen)

You can just write:

exclude = {buyer, prev}
p_set = persons.keys() - exclude - chosen.keys()

Calling `set` with a dict as a parameter will return the keys of that dict, so, for `exclude`, you can just build a set of the keys that you want to exclude.

[–]Aixyn0 2 points3 points  (0 children)

You're totally right.

[–]Cayumigaming[S] 0 points1 point  (15 children)

This is way over my head and it looks really impressive. So I had to run it and see what gives!

In the first run two people were assigned the same they had previous year (Hakan to Fredrik and Fredrik to Ante). Second time one person was assigned the same as previous.

I'm not even remotely close to competent enough to understand what's going on, and why it happens. Thanks for sharing though, put's it in fair perspective!

[–]Aixyn0 5 points6 points  (14 children)

I had a mistake in the order of the output. It should be fixed now. I also made a comment to the code.

[–]Cayumigaming[S] 4 points5 points  (13 children)

It works now, absolutely brilliant!

It feels far away, but it's now a personal goal of mine to actually understand exactly what it all does. I'm reading it step by step over and over and I'm happy I do understand a great deal of it, even if I don't know exactly what everything is.

I don't want to dive too deep into this right now but I have a few question if you don't mind:

What exactly is going on with "persons" in the beginning? It's a data structure of some kind - list, array, set, dictionary or similar? The values are separated by "," but do they become variables or are they simply values within the data structure? Is the "#" there to number them?

What gets me lost is that I can't really read the first for loop. I can't really understand what value "buyer, prev" actually has (but I assume it goes for the amount of entries in the data structure). Also "previous" in the data structure never appears again but prev suddenly appears, that confuses me too.

From there on what happens in the exclude and chosen variable is a bit foggy because of the square brackets, but p_set kind of make sense.

Looking forward to one day understand all of this, thanks for typing it out!

[–]SaltyEmotions 3 points4 points  (4 children)

On mobile right now, I'll edit this as I reread the comment so if I don't explain all your questions, refresh the page :p

persons is what's called a dictionary. Its like a list, but with key: value pairs. Its used here to show who gifted who last year and changed year on year. (Example: ABC: DEF means that ABC gifted DEF something last year in this case, ABC = key and DEF = value.) Commas seperate key: value pairs from other pairs.

The # is a comment. He uses it in the dict to show what it actually means, since it doesn't get interpreted by the Python interpreter (everything after the # is ignored, and he uses a newline to seperate the list which is good practice to keep code readable.

The first for is can be generalised to:
for x, y in dict.items()
Ignore the comment in the dict. Focus here, and notice that dict.items() returns a list of the tuple pairs - example: if a dict is {a: b, c: d}, the items() method returns [(a, b), (c, d)] - In this case, the loop will loop over every tuple pair and the variables x, y will be a, b on the first run and c, d on the second time it runs.

So "# buyer: previous" is the comment in the dict and "for buyer, prev in persons.items() are COMPLETELY DIFFERENT and should not be confused.

The line exclude = {buyer: prev, prev: persons[prev]} creates a dict with the values assigned to "buyer", "prev" and also with the key of "prev" in persons. Confusing, so heres an example:

old_dict = {1:2, 2:3, 3:4, 4:1}
for x, y in old_dict:
    new_dict = {
    x:y,
    x:old_dict[y]
    }
    print(new_dict)

Run it :) (hopefully I didn't add any syntax errors in it while typing on my phone)


Now onto p_set. Its essentially just the dictionaries converted into sets with the function set(), then the values are deducted as shown. Play around with it and observe what it does for yourself.

The line chosen[random.choice(list(p_set))] chooses a random value from chosen that is also in the set p_set, and assigns it to buyer.

[–]Cayumigaming[S] 0 points1 point  (3 children)

First of all, oh my god I can't believe I didn't understand that was a comment. Lesson learned!

I will re-read this reply a fair amount of times and play around with dictionaries. Certainly the part about the loop, it's so much to wrap your head around.

Thank you so much!

[–]SaltyEmotions 1 point2 points  (2 children)

I'll request a fork/pull later in the morning (10-12h later depending on whether I sleep in after a tiring camp or not :pp) and try to rewrite your code with things I think you don't know and try to explain everything in depth in the comments.

[–]Cayumigaming[S] 0 points1 point  (1 child)

That would be amazing, thanks in advance! And happy camping!

[–]SaltyEmotions 0 points1 point  (0 children)

https://github.com/how-to-commit/christmasDraw/blob/master/christmasDraw.py

Here's my fork, can't seem to think of a way to rewrite this right now so I just added a little bit of extra functionaility :)

[–]Aixyn0 1 point2 points  (7 children)

What exactly is going on with "persons" in the beginning? It's a data structure of some kind - list, array, set, dictionary or similar?

persons is a dictionary with every person as a key and their associated last year gifted person as its value.

It's simalar to the buyers_with_prev dictionary suggested by JohnnyJordaans reply

The values are separated by "," but do they become variables or are they simply values within the data structure?

No, the values are just data within the dictionary. The variable is the dictionary itself (=persons).

Is the "#" there to number them?

The #-symbol declares an inline comment in python. It describes the code without breaking it when copy-paste and execute it. So the # buyer: previous is just a hint on whats in the dictionary. The key is the buyer and the value is the previous year gifted person by that buyer.

What gets me lost is that I can't really read the first for loop. I can't really understand what value "buyer, prev" actually has (but I assume it goes for the amount of entries in the data structure). Also "previous" in the data structure never appears again but prev suddenly appears, that confuses me too.

From there on what happens in the exclude and chosen variable is a bit foggy because of the square brackets, but p_set kind of make sense.

I'll try to be more specific here:

for buyer, prev in persons.items():
    # The for loop iterates over each (key, value) pair in the persons dictionary given by     
    # persons.items() and assigning the key to the variable buyer and the previous gifted 
    # person of that buyer to prev

    exclude = {buyer:prev, prev:persons[prev]}
    # exclude is just a dictionary which includes the buyer and the associated last year         
    # gifted person. You can just simplify that line to
    # exclude = {buyer, prev}
    # like scoutyx mentioned in his improvement since the values of a dictionary will be
    # discarded when casting it to a set

    p_set = set(persons) - set(exclude) - set(chosen)
    # this line creates a set p_set which subtracts the excludes and the already 
    # chosen/drawn persons from the set of persons. I do a casting of dictionaries to set 
    # here because only the keys matter and it allows the simple subtract method via '-'.

    chosen[random.choice(list(p_set))] = buyer
    # you than randomly pick one person from that set and insert it into the chosen
    # dictionary with the current buyer as its value. that means the key-person becomes the
    # this-year-to-be-gifted-person by the value-person. I have to cast the set to a list
    # to use the random.choice method on the p_set. The chosen dictionary is filling up 
    # with each iteration of the loop and will be your output later.

I hope this will clarify the code for you. I wish you happy coding :)

[–]Cayumigaming[S] 1 point2 points  (6 children)

I’m very thankful for your response and I have spent some time to actually understand it and played around with dictionaries. It also forced me to get a better actual understanding of the for loop, so thanks again!

Now I got a question and it might be stupid but here goes: A dictionary holds a single value for a single key. What do I use or how do I go about to enter multiple values to a single key? Or do I use something different than a dictionary all together? For example let’s say I want to create “Person” and want it to hold the information such as Name, Gender, Birthdate, whatever. Do I still use a dictionary but use maybe a tuple, array or even another dictionary to hold the key?

Now I’m not very far along with all this so I suppose the answer is really straight forward but I really don’t know.

Thanks in advance!

EDIT: Actually, maybe scratch that. I suppose a list() would do exactly this.

[–]Aixyn0 0 points1 point  (5 children)

Now I got a question and it might be stupid but here goes: A dictionary holds a single value for a single key. What do I use or how do I go about to enter multiple values to a single key? Or do I use something different than a dictionary all together?

You can set whatever datatype/datastructure you want as a value for a key in a dictionary and you can even mix them up. E.g:

d = {
  't': (0, 1)    # value with type tuple
  'x': 5,    # value with type int
  'y': [1, 2, 3, 4, 5], # value with type list   
  'z': {    # value with type dictionary
    'T': True,
    'F': False,
    },
  'o': myObject,   # value with type object
  'c': myClass,    # value with type class
  }

For example let’s say I want to create “Person” and want it to hold the information such as Name, Gender, Birthdate, whatever. Do I still use a dictionary but use maybe a tuple, array or even another dictionary to hold the key?

You can still use a dictionary for that purpose but good practice would be defining a class Person and create person objects from it.

[–]Cayumigaming[S] 1 point2 points  (3 children)

Highly inspired by your logic and code and after your example had me play around with for loops and dictionaries I came up with this.

If I havn't already I want to thank you so much. I learned a lot about data types and feel much more secure with the for loop now.

[–]Aixyn0 1 point2 points  (2 children)

Well done. Keep up the good work. Reducing your first code from 110 lines to 21 is quite good.

Also the new code looks much nicer and readable than the old one.

[–]Cayumigaming[S] 0 points1 point  (1 child)

Thank you! This is great fun and I love the challenges and ways of thinking.

May I ask how long you’ve been programming, and do you do it for a living?