1

Okay, so I have a bit of a problem, Im new to python sorry.

I am trying to sort a list by score, which is a number, but if there is a draw I need to sort them by the length of their name, the shorter the name the higher the rank.

So I have this list here

11 Jenny
8 Adam
10 Mark
8 Ada

and when I use this code here on it, it just comes back with

11 Jenny
10 Mark
10 Mark
10 Mark

def sort(names, counts):
    newArr = []
    newNames = names
    newCount = counts
    for x in range(0, len(names)):
        newArr.append(findBiggest(newNames, newCount))
        for z in range(0, len(names)):
            name = newArr[len(newArr) - 1].split(" ")[1]
            print name 
            if names[z] == name:
                tempArr1 = newNames
                tempArr2 = newCount
                newNames = []
                newCount = []
                for y in range(0, len(tempArr1)):
                    if y != z:
                        newNames.append(tempArr1[y])
                        newCount.append(tempArr2[y])
    return newArr
def findBiggest(names, counts):
    biggest = 0;
    for x in range(0, len(counts)):
        if int(counts[x]) > biggest:
            biggest = int(counts[x])
    biggestCountArr = [[], []]
    for x in range(0, len(counts)):
        if int(counts[x]) == biggest:
            biggestCountArr[0].append(counts[x])
            biggestCountArr[1].append(names[x])
    if len(biggestCountArr[0]) == 1:
        return str(biggestCountArr[0][0]) + " " + biggestCountArr[1][0]
    else:
        return smallestLength(biggestCountArr)
def smallestLength(twoDArr):
    names = twoDArr[1]
    shortestLen = 0
    for x in range(0, len(names)):
        if len(names[x]) > shortestLen:
            shortestlen = len(names[x])
    for x in range(0, len(names)):
        if len(names[x]) == shortestLen:
            return str(twoDArr[0][x]) + " " + twoDArr[1][x]

Just so you know

11 Jenny
8 Adam
10 Mark
8 Ada

should come out as

11 Jenny
10 Mark
8 Ada
8 Adam
FabianCook
  • 20,269
  • 16
  • 67
  • 115

3 Answers3

11
lst=[(11, "Jenny"),(8, "Adam"),(10, "Mark"),(8, "Ada")]
lst.sort(key=lambda x: (-x[0],len(x[1])) )
print (lst) # [(11, 'Jenny'), (10, 'Mark'), (8, 'Ada'), (8, 'Adam')]

The list method sort and the builtin function sorted accept a keyword argument key which is given a callable. Basically, for every element in the sequence, that element is passed to the key function and the return value of that function is actually what python uses to determine ordering when sorting. So, in the above, I use lambda to construct a function which returns a tuple from the input elements. The tuple is ordered first_element, lenth_of_second_element.

When tuples (or lists for that matter) are compared, it's much like comparing a string. You look at the first element, if they're the same, you continue on to look at the second element, then the third and so on until one element is greater than the other. e.g.

(1,2,3,4) > (1,2,3,3) #True

This ends up being handy for sorting in very interesting ways.

I suppose to round this out, I should mention that the algorithm that python uses to sort is stable. This means that if you sort by keyA and then you sort by keyB, two elements which compare equal based on keyB will maintain the order they had after the sort using keyA. In other words, a sort doesn't change the order of equal valued elements. So, the above could also be accomplished like this:

lst.sort(key=lambda x:len(x[1]))  #sort by length of names
lst.sort(key=lambda x:x[0], reversed=True) #sort by score (highest first instead of regular lowest first)

And I suppose no answer is complete without a link to something which explains it more elegantly. (Specifically, see the section on "Key Functions")

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • Will this sort it based on number then length of name? – FabianCook Jul 31 '12 at 02:06
  • Magic incantations are rarely helpful. You might want to spend some time _explaining_ this! – paxdiablo Jul 31 '12 at 02:08
  • 1
    @SmartLemon -- Yes. The comparison is done via tuples. I basically map out each element into the list as `(first_element, len_of_name)`. Then those tuples are sorted. sequences are ordered much like you would order strings. The first element is compared, then the next, then the next ... – mgilson Jul 31 '12 at 02:08
  • I need the reverse scoring, larger score goes first, smaller length of name if draw – FabianCook Jul 31 '12 at 02:13
  • @SmartLemon -- updated. `x[0] -> -x[0]` (Now big numbers come first). – mgilson Jul 31 '12 at 02:16
  • How do I get the values from this new list? so I want to get the first item and I want to get the number and the name? lst[z][0] in a for loop to get the number? – FabianCook Jul 31 '12 at 02:18
  • @SmartLemon -- the easiest way is to use python's builtin sequence unpacking. So, to get the highest scoring person, you could do `score, name = lst[0]`. If you don't want to use sequence unpacking you could do `high_score = lst[0][0]`, and `high_scorer = lst[0][1]` (all of this is after the sorting of course). – mgilson Jul 31 '12 at 02:22
  • @mgilson +1 Convenient way to change the ordering result in your lambda function using +/- – aneroid Jul 31 '12 at 02:31
  • 2
    @aneroid -- Yeah, that always works with numeric input. If you're not using numeric input, you have to rely on the fact that python sorting is stable and sort twice (once with `reversed=True` and once without). – mgilson Jul 31 '12 at 02:34
  • @mgilson whoops, I didn't realise that it was _already_ smaller name first in your code. Deleted my comment. `>>> lst.sort(key=lambda x: (-x[0],-len(x[1])) )` gives `[(11, 'Jenny'), (10, 'Mark'), (8, 'Adam'), (8, 'Ada')]` (larger name first) – aneroid Jul 31 '12 at 02:34
  • Thank you very much :D. I learn't something, my main language is Java so there's a few differences. Worked perfectly. – FabianCook Jul 31 '12 at 03:10
0

You'd have to iterate over each line and use the sort method on them

#open the file and read through the lines.
lines = open('yourfile.txt').readlines()

#iterate over each line, and split along the spaces 
pairs =[]
for line in lines:
   split_string = line.split('')
   num = int(split_string[0])
   pairs.append((num, split_string[1:])

#sort through the pairs (from mgilsons answer)
pairs.sort(key=lambda x: (x[0],len(x[1]))

edit: actually misread the question.

TankorSmash
  • 12,186
  • 6
  • 68
  • 106
  • You'll probably want to have the tuples in `pairs` start with an integer (i.e. `int(split_string[0])`) otherwise you'll be in for a surprise when you come to '-1' and '-100'. – mgilson Jul 31 '12 at 02:28
0
aList = [
    (11, 'Jenny'),
    (8, 'Adam'),
    (10, 'Mark'),
    (8, 'Ada'),
]

aList.sort(lambda x, y: y[0] - x[0])
print aList
cdtits
  • 1,118
  • 6
  • 7