Wes
Wes

Reputation: 75

Looping over a dictionary to display key and values in order of a value

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!

Upvotes: 4

Views: 164

Answers (6)

Stefan Pochmann
Stefan Pochmann

Reputation: 28596

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

Upvotes: 0

Shashank
Shashank

Reputation: 13869

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.

Upvotes: 4

Padraic Cunningham
Padraic Cunningham

Reputation: 180401

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

Upvotes: 2

Jsevillamol
Jsevillamol

Reputation: 2543

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])

Upvotes: 2

Adam Smith
Adam Smith

Reputation: 54183

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))

Upvotes: 1

poke
poke

Reputation: 387607

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()]

Upvotes: 2

Related Questions