4

I am attempting to loop over a dictionary with a for loop to display keys and values in order of one of the value elements(the batting order number).

I can print the keys and values in the intended format, but I cannot figure out how to get the lines in the correct batting order.

I need to complete this task with logic in a for loop and without the use of lambda or a function. Here is what I have so far:

print ('Rays starters' + "\n")
rays_starters = {
    'DeJesus' : ['DH', 6, 299],
    'Loney' : ['1B', 4, 222],
    'Rivera' : ['C', 9, 194],
    'Forsythe' : ['2B', 5, 304],
    'Souza Jr' : ['RF', 2, 229],
    'Longoria' : ['3B', 3, 282],
    'Cabrera' : ['SS', 7, 214],
    'Kiermaier' : ['CF', 1, 240],
    'Guyer' : ['LF', 8, 274] }
for player in rays_starters:
    print (player + str(rays_starters[player]))
print ('\n' + 'Today\'s lineup' + '\n')
for player in rays_starters:
    batting_order = rays_starters.get(player)
    print('Batting ' + str(batting_order[1]) + ' : ' + str(batting_order[0]) + ' ' + player + ' ,current avg: ' + str(batting_order[2]))   

The output should look like this:

Rays starters

DeJesus ['DH', 6, 299]
Loney ['1B', 4, 222]
Rivera ['C', 9, 194]
Forsythe ['2B', 5, 304]
Souza Jr ['RF', 2, 229]
Longoria ['3B', 3, 282]
Cabrera ['SS', 7, 214]
Kiermaier ['CF', 1, 240]
Guyer ['LF', 8, 274]

Today's lineup

Batting 1 : CF Kiermaier ,current avg: 240
Batting 2 : RF Souza Jr ,current avg: 229
Batting 3 : 3B Longoria ,current avg: 282
Batting 4 : 1B Loney ,current avg: 222
Batting 5 : 2B Forsythe ,current avg: 304
Batting 6 : DH DeJesus ,current avg: 299
Batting 7 : SS Cabrera ,current avg: 214
Batting 8 : LF Guyer ,current avg: 274
Batting 9 : C Rivera ,current avg: 194

My output does in fact look exactly like this with the exception of the batting order being out of order. Please help me get on the right track and remember I am trying to learn here so any helpful criticism is welcome!

Alexander
  • 105,104
  • 32
  • 201
  • 196
Wes
  • 75
  • 1
  • 1
  • 5
  • @Padraic Yes I am aware of that but printing the contents in a particular order is possible and I am trying to learn how to do that properly.. – Wes Jun 01 '15 at 20:02
  • Do you mean sorting the values by a key? Or perhaps retrieving them in the order that they were put into the dictionary? – Shashank Jun 01 '15 at 20:03
  • without sorting with a key which would need a lambda or a function call using itemgetter then you would need to create a list manually with the names in the order you want, iterate over the list and do the lookups – Padraic Cunningham Jun 01 '15 at 20:03
  • @Shashank, the output is based on the batting orde,r the second element of each list – Padraic Cunningham Jun 01 '15 at 20:04
  • My thinking is that there should be some way to access the number in the 2nd element of the values which is the batting order and compare it to a list 1-9 to print in that order but I keep getting brain twisted trying to determine how to do that. – Wes Jun 01 '15 at 20:08
  • have you tried using the ordered dictionary in python? – sudhishkr Jun 01 '15 at 20:09
  • A dopey solution - which maybe they are looking for, would be make an outer loop through range(1,10), make an inner loop to go through the players, and print the player that matches the outer index. – Eric Appelt Jun 01 '15 at 20:12

6 Answers6

4

Here's an efficient way since we know there are going to be 9 batters.

lineup = [None] * 9
for player, stats in rays_starters.items():
    lineup[stats[1]-1] = player, stats
print ('\nToday\'s lineup\n')
for player, batting_order in lineup:
    print('Batting ' + str(batting_order[1]) + ' : ' + str(batting_order[0]) + ' ' + player + ' ,current avg: ' + str(batting_order[2]))

All we are doing is initializing an array of 9 elements and using the batting order to map the player and stats as a tuple to the correct array index. Then we loop through the array of player and statistics tuples, and print the desired formatted output. This is O(n).

This concept is basically derived from Radix sort, or, more specifically, a very simple case of Counting sort where all the frequences are 1 and the "key function" is just subtracting 1 from the batting order to get an array index.

As @PadraicCunningham notes in the comments, this can theoretically be used for any number of batters by using the len function.

Shashank
  • 13,713
  • 5
  • 37
  • 63
2

Dictionaries do not have an order, so you cannot sort them. You can however iterate over its values in a sorted manner. For this, you can use sorted() and a key function that specifies how to get the value when passed a (key, value) tuple:

for player, batting in sorted(rays_starters.items(), key=lambda x: x[1][1]):
    print('Batting {1} : {0} {player}, current avg: {2}'.format(*batting, player=player))

For your rays_starters dictionary, this will yield the following result:

Batting 1 : CF Kiermaier, current avg: 240
Batting 2 : RF Souza Jr, current avg: 229
Batting 3 : 3B Longoria, current avg: 282
Batting 4 : 1B Loney, current avg: 222
Batting 5 : 2B Forsythe, current avg: 304
Batting 6 : DH DeJesus, current avg: 299
Batting 7 : SS Cabrera, current avg: 214
Batting 8 : LF Guyer, current avg: 274
Batting 9 : C Rivera, current avg: 194

If you can’t specify such a key function, you will have to implement the sorting on your own. For this, you can first turn the dictionary into a list first which you then sort. In order to not need a key function, you should construct that list so the value you want to sort by is the first in the list:

data = []
for player, batting in rays_starters.items():
    data.append((batting[1], player, batting[0], batting[2]))

# now sort the list
data.sort()

# and iterate and print
for player in data:
    print('Batting {0} : {1} {2}, current avg: {3}'.format(*player))

You can also create the data list using a list comprehension:

data = [(b[1], p, b[0], b[2]) for p, b in rays_starters.items()]
Community
  • 1
  • 1
poke
  • 369,085
  • 72
  • 557
  • 602
  • The OP cannot use a lambda or a function – Padraic Cunningham Jun 01 '15 at 20:06
  • This is of course a correct answer but unfortunately I already know how to do it this way. I need to try to figure out how to do it without lambda. – Wes Jun 01 '15 at 20:07
  • @poke I apologize because I did not fully read through your answer after seeing the lambda at the top. Your second solution may actually be exactly the idea of what I am tying to do! – Wes Jun 01 '15 at 20:20
  • @Wes No need to apologize because I also missed the “no lambda or function” requirement at first; that’s why I added another solution to my answer ;) – poke Jun 01 '15 at 20:21
2

If you cannot use min, lambdas, sorted and other function calls etc manually find the player with the lowest playing batting number starting from the lowest:

out = []
cp = rays_starters.copy()
# keep going while the dict is not empty
while cp:
    mn = float("inf")
    it = None
    # iterate over the item to find the min each time
    # from remaining items
    for k, v in cp.items():
        if v[1] < mn:
            mn = v[1]
            it = (k, v)
    # append current it k/v pair which has the lowest 
    # batting number         
    out.append(it)
    # remove the key so we can get the next lowest
    del cp[it[0]]

for k,v in out:
    print("{} {}".format(k,v))

Output:

Kiermaier ['CF', 1, 240]
Souza Jr ['RF', 2, 229]
Longoria ['3B', 3, 282]
Loney ['1B', 4, 222]
Forsythe ['2B', 5, 304]
DeJesus ['DH', 6, 299]
Cabrera ['SS', 7, 214]
Guyer ['LF', 8, 274]
Rivera ['C', 9, 194]

Or without copying:

out = []
seen = set()
# for every player in the dict
for _ in rays_starters):
    mn = float("inf")
    it = None
    # again get min each time based on the batting number
    for k, v in rays_starters.items():
        # this time we make sure we have not already used
        # the player 
        if v[1] < mn and k not in seen:
            mn = v[1]
            it = (k, v)
    out.append(it)
    # add the name of the player that matches our current min
    seen.add(it[0])

for k,v in out:
    print("{} {}".format(k,v))

If you can actually sort or use sorted use the batting number as the key and just sort the items:

temp = {v[1]:[k]+v for k, v in rays_starters.items()}

for k ,v in sorted(temp.items()):
    print("{} {}".format(v[0], v[1:]))

 Kiermaier ['CF', 1, 240]
Souza Jr ['RF', 2, 229]
Longoria ['3B', 3, 282]
Loney ['1B', 4, 222]
Forsythe ['2B', 5, 304]
DeJesus ['DH', 6, 299]
Cabrera ['SS', 7, 214]
Guyer ['LF', 8, 274]
Rivera ['C', 9, 194]

If the batting always starts at 1:

temp = {v[1]:[k]+v for k, v in rays_starters.items()}

for k  in range(1,len(rays_starters)+1):
    v = temp[k]
    print("{} {}".format(v[0], list(v[1:])))

You can unpack to print:

temp = {v[1]:[k]+v for k, v in rays_starters.items()}

for k in range(1,len(rays_starters)+1):
    name, nm, btn, avg = temp[k]
    print("Batting: {} {} {}, current avg: {}".format(btn, name, nm, avg))

Output:

Batting: 1 Kiermaier CF, current avg: 240
Batting: 2 Souza Jr RF, current avg: 229
Batting: 3 Longoria 3B, current avg: 282
Batting: 4 Loney 1B, current avg: 222
Batting: 5 Forsythe 2B, current avg: 304
Batting: 6 DeJesus DH, current avg: 299
Batting: 7 Cabrera SS, current avg: 214
Batting: 8 Guyer LF, current avg: 274
Batting: 9 Rivera C, current avg: 194
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • This looks like it has promise for what I am trying to do. I'm going to need a few moments to try to digest. – Wes Jun 01 '15 at 20:13
2

A quite simply and quite ineficcient way of doing this is to iterate len(rays_starters) times over the array and only print the result that matchs the order each time.

for i in range (1, len(rays_starters)+1):
    for player in rays_starters:
        if rays_starters[player][1] == i:
            print('Batting ' + str(rays_starters[player][1]) + ' : ' + \
            rays_starters[player][1] + ' ' + player + ' ,current avg: ' + \
            rays_starters[player][1])
Jsevillamol
  • 2,425
  • 2
  • 23
  • 46
1

Seems like a better way to handle this would be a (sorted) list of namedtuples

from collections import namedtuple

Player = namedtuple("Player", ['name', 'position', 'order', 'avg'])
players = [Player(*info) for info in [("DeJesus", "DH", 6, 299),
                                      ('Loney', '1B', 4, 222),
                                      ...]]

players.sort(key=lambda p: p.order)

for player in players:
    print("Batting {p.order} : {p.position} {p.name}, current avg {p.avg}".format(p=p))
Adam Smith
  • 52,157
  • 12
  • 73
  • 112
  • You can strip out the `lambda` used as a `sort` key by simply listing all your players in order inside the `players` list comprehension. Alternatively you could use `operator.attrgetter('order')` – Adam Smith Jun 01 '15 at 20:12
  • The bolded part of the question states that lambdas and functions may not be used. Weird, I know – Shashank Jun 01 '15 at 20:13
  • @Shashank I addressed that concern in my comment, above. The `lambda` in this code is simply to keep me from having to parse his list of players by hand! :P – Adam Smith Jun 01 '15 at 20:14
  • Although this doesn't help so much with the task at hand it does help my comprehension of lambda so thank you for this answer. – Wes Jun 01 '15 at 20:17
  • @Wes I think the biggest issue is that this is an XY problem! You're looking for help with your solution instead of help with your problem. The solution you've chosen is to sort a dictionary every iteration. It's the wrong solution! I'd strongly advise building an object (`namedtuple` is easiest, but you can always define a class), building a list of them, and sorting exactly once. – Adam Smith Jun 01 '15 at 20:19
  • 1
    @Adam I agree with you but as I am a student I don't have the ability to decide the parameters of my solution but rather must follow the rules of the assignment. I only posted this as a "my head is gonna explode" last resort. I have solved this problem several different ways over the last few days including using lambda which is definitely the most succinct but solving the way the professor wants it solved has proven extremely elusive. – Wes Jun 01 '15 at 20:26
0

I'm late to the party, but I wanna add this hack anyway:

>>> for x in {v[1]:'Batting {2} : {1} {0} ,current avg: {3}'.format(k,*v)
              for k,v in rays_starters.items()}.values():
        print(x)

Batting 1 : CF Kiermaier ,current avg: 240
Batting 2 : RF Souza Jr ,current avg: 229
Batting 3 : 3B Longoria ,current avg: 282
Batting 4 : 1B Loney ,current avg: 222
Batting 5 : 2B Forsythe ,current avg: 304
Batting 6 : DH DeJesus ,current avg: 299
Batting 7 : SS Cabrera ,current avg: 214
Batting 8 : LF Guyer ,current avg: 274
Batting 9 : C Rivera ,current avg: 194

Don't do this, though, it relies on the actual but not guaranteed order of dictionary items. I merely post it for edutainment.

On the other hand, it still works if you remove some of the players, unlike the accepted answer :-P

Stefan Pochmann
  • 27,593
  • 8
  • 44
  • 107