2

I'm not sure what to title this so suggestions to the title are also welcome. I'm creating a program that will read data from a file and print it in order.

The file will always contain data based on this:

  • Name:1,2,3
  • Name_2:4,5,6
  • Name_3:10,9,8

Here's my code for getting the data from the file:

def getData(file):
    data = {}
    try:
        with open(file + ".txt", "r") as file:
            for line in file:
                build = line.split(":")
                data[build[0]] = build[1]
    except FileNotFoundError:
        with open(file + ".txt", "a") as file:
            pass
    return data

The returned data is :

data = {"Name": "1,2,3\n", "Name_2": "4,5,6\n", "Name_3": "10,9,8\n"

Now that I've got the data, I'm trying to "sort" it, dictionaries aren't really sortable so I used "OrderedDict." Heres the code:

def sortData(data, choice):
    ## Sort By Average
    if choice == "Average":
        for name, score in data.items():
            amount = 0
            for i in re.finditer("[0-9]{1,2}", score):
                amount += 1
            average  = eval(re.sub(",","+",score))/amount
            data[name] = str(average) + "\n"
        od_data = collections.OrderedDict(sorted(data.items(), key=lambda t:float(t[1]), reverse=True))
        for name, score in od_data.items():
            sys.stdout.write("%s: %s" % (name, score))
    ## Sort By Highest - Lowest
    elif choice == "Highest - Lowest":
        od_data = collections.OrderedDict(sorted(data.items(), key=lambda t:t[1], reverse=True))
        for name, score in od_data.items:
            sys.stdout.write("%s: %s" % (name, score)

When the choice is "Average", the data is printed as it is supposed to and within order. When the choice is "Highest - Lowest", the data is partially printed out in order:

  • Name_2:4,5,6
  • Name:1,2,3
  • Name_3:10,9,8

It works as it should, only if the first "number" is not "10", for some reason it seems to register in the program as "1" and "0" and therefore ends up being as the lowest entry.

Max
  • 729
  • 5
  • 15

2 Answers2

0

Instead of using re and eval, you could just split the strings by , and map to int (or float), is possible directly when loading the file. Then the rest gets much easier.

>>> data = {"Name": "1,2,3\n", "Name_2": "4,5,6\n", "Name_3": "10,9,8\n"}
>>> data = {k: [int(x) for x in v.split(',')] for k, v in data.items()}
>>> collections.OrderedDict(sorted(data.items(), key=lambda item: sum(item[1]) / len(item[1]), reverse=True))
OrderedDict([('Name_3', [10, 9, 8]), ('Name_2', [4, 5, 6]), ('Name', [1, 2, 3])])

In your second part, for choice == "Highest - Lowest", is not entirely clear how you want to sort. If you want to sort the lists by their natural order -- i.e. compare be the first element, and if those are equal by the second, and so on -- then sorted(data.items(), key=lambda t:t[1], reverse=True)in itself is correct, butt[1]fromdata` is still a string, so you are just sorting by the lexicographical order of those strings. You have to convert to lists of integers first, as above.

If instead you want to sort by the minimum of maximum element of those lists, you can try this:

>>> collections.OrderedDict(sorted(data.items(), key=lambda item: min(item[1]))) # or max for maximum

Also note that you do not need to wrap the result of the sort into an OrderedDict at all, since you just iterate the items anyway. Just work with the result of sorted directly. Also, I suggest changed the data to lists of ints before calling the function. All in all, you could change your code to something like this:

def sortData(data, choice):
    sorters = {"Average": lambda item: sum(item[1]) / len(item[1]),
               "Highest - Lowest": lambda item: item[1],
               "Minimum": lambda item: min(item[1]),
               "Maximum": lambda item: max(item[1])}
    if choice in sorters:
        sorter = sorters[choice]
        for name, score in sorted(data.items(), key=sorter, reverse=True):
            print("%s: %s" % (name, score))
    else:
        print("Unknown choice: " + choice)

data = {"Name": "1,2,3\n", "Name_2": "4,5,6\n", "Name_3": "10,9,8\n"}
data = {k: [int(x) for x in v.split(',')] for k, v in data.items()}
sortData(data, "Minimum")
tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • Thanks for this, I've implemented this into my code: `Jeff: [10, 10, 10]Max: [9, 0]Harry: [5, 4]Henry: [5, 5, 5]` and this is produced. I can work with this and print it in the correct format, but, as you can see, Harry is printed before Henry. How can I make it so it takes into account all the numbers, so to speak. – Max Mar 21 '16 at 21:08
  • I could order the list from highest to lowest, although I don't think that will completely help, any suggestions? – Max Mar 21 '16 at 21:10
  • @SylentNyte What did you order this by? Maximal value? In this case, there is a tie between Harry and Henry. How do you want to "break" that tie? Total sum? Number of highest elements? Length of list? – tobias_k Mar 21 '16 at 21:11
  • When used minimal value : Jeff: [10, 10, 10]Henry: [5, 5, 5]Harry: [5, 4]Max: [9, 0], I'm not sure why that is. To "break" it, essentially, Henry would have to be first by assuming that Harry's is 5,4(,0). If that makes sense, sorry if I don't, fairly novice python user here – Max Mar 21 '16 at 21:56
  • @SylentNyte Sorry, no, does not make very much sense to me. As it is now, it sorts correctly: the min value of Jeff if 10, for Henry 5, Harry 4 and lastly Max with 0. But the problem is with ties. For this case, you have to make up your mind and clearly define how to sort then in this case, i.e. what to do when the min/max value for two lists is the same. – tobias_k Mar 21 '16 at 22:00
  • Update : After some experimenting, I've made it work. I've used your `data = {k: [int(x) for x in v.split(',')] for k, v in data.items()}` (extremely helpful thank you), but instead of the OrderedDict's you gave me I used this `od_data = collections.OrderedDict(sorted(data.items(), key=operator.itemgetter(1), reverse=True))` and it works as wanted, including ties. :D Thanks – Max Mar 22 '16 at 01:49
0

The problem was that the program wouldn't order them based on all the values. I used a suggestion for @tobias_k whih was very helpful and implented another code with it:

Finalised Code:

data = {k: [int(x) for x in v.split(',')] for k, v in data.items()}
        od_data = collections.OrderedDict(sorted(data.items(), key=operator.itemgetter(1), reverse=True))
        print(od_data)

Now it prints:

OrderedDict([('Jeff', [10, 10, 10]), ('Max', [9, 0]), ('How', [6, 2, 3]), ('What', [6, 1]), ('Jefferson', [6]), ('Henry', [5, 5, 5]), ('Why', [5, 5, 4]), ('Harry', [5, 4])])

(I added a few examples to properly test it.)

Max
  • 729
  • 5
  • 15