遍历字典以按值顺序显示键和值

4

我正在尝试使用 for 循环遍历字典,以按照其中一个值元素(打击顺序号)的顺序显示键和值。

我可以按照预期格式打印键和值,但我无法弄清楚如何按照正确的打击顺序排列行。

我需要在 不使用 lambda 或函数 的情况下,在 for 循环中完成此任务。以下是我目前的代码:

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

输出应该像这样:
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

我的输出实际上与此完全相同,唯一的区别是击球顺序错了。请帮助我找到正确的方向,并记住我正在尝试学习,因此任何有用的批评都受欢迎!


@Padraic 是的,我知道,但是按特定顺序打印内容是可能的,我正在努力学习如何正确地做到这一点。 - Wes
不使用需要lambda或函数调用的键进行排序,您将需要手动创建一个包含按照您想要的顺序排列的名称的列表,然后遍历该列表并进行查找。 - Padraic Cunningham
我的想法是应该有一种方法可以访问值中的第二个元素中的数字,这是击球顺序,并将其与1-9列表进行比较,以按顺序打印,但我一直在努力确定如何做到这一点。 - Wes
你尝试过在Python中使用有序字典吗? - sudhishkr
一个笨拙的解决方案——也许他们正在寻找的,是通过range(1,10)创建一个外部循环,创建一个内部循环来遍历玩家,并打印与外部索引匹配的玩家。 - Eric Appelt
显示剩余2条评论
6个回答

4

既然我们知道会有9名击球手,这是一种有效的方法。

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

我们所做的就是初始化一个包含9个元素的数组,并使用击球顺序将球员和统计信息作为元组映射到正确的数组索引。然后,我们循环遍历球员和统计信息的数组,并打印所需格式化的输出结果。这是O(n)。
这个概念基本上是从 基数排序推导出来的,或者更具体地说,是 计数排序的一个非常简单的情况,在这种情况下,所有频率都为1,并且“关键函数”只是从击球顺序中减去1以获取数组索引。
正如@PadraicCunningham在评论中指出的那样,理论上可以使用len函数将其用于任何数量的击球手。

我真的很喜欢这个答案,我正在努力阅读并确保我完全理解它的工作原理。 - Wes
1
你不需要硬编码球员数量,可以使用 [None] * len(rays_starters) - Padraic Cunningham

2

字典没有顺序,因此无法对它们进行排序。但是您可以以排序的方式遍历其值为此,您可以使用sorted()和一个键函数,指定在传递(key,value)元组时如何获取该值:

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

针对您的rays_starters字典,这将产生以下结果:
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

如果你不能指定这样一个关键函数,那么你就需要自己实现排序。为此,你可以先将字典转换成列表,然后对其进行排序。为了不需要关键函数,你应该构建这个列表,使你想要排序的值排在列表的第一位:

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

您也可以使用列表推导式创建data列表:
data = [(b[1], p, b[0], b[2]) for p, b in rays_starters.items()]

该操作者无法使用 lambda 表达式或函数。 - Padraic Cunningham
这当然是一个正确的答案,但不幸的是我已经知道如何用这种方法做了。我需要尝试弄清楚如何在没有 lambda 的情况下完成它。 - Wes
@poke 对不起,因为我看到顶部的lambda后没有完全阅读您的答案。您的第二个解决方案实际上可能正是我想要做的! - Wes
@Wes 不用道歉,因为我一开始也没注意到“不能使用lambda或函数”的要求;这就是为什么我在我的答案中添加了另一个解决方案 ;) - poke

2

如果您无法手动使用min、lambdas、sorted和其他函数调用等方式,可以从最低的开始找到打击次数最少的球员:

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

输出:

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]

或者不复制:

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

如果你能够实际排序或使用排序过的项,可以将击球号作为键,然后只需对这些项进行排序:

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]

如果击球始终从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:])))

你可以解压以打印:
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))

输出:

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

这看起来很有前途,符合我所尝试的目标。我需要一些时间来消化。 - Wes

2
一种非常简单但效率低下的方法是循环遍历rays_starters数组,并且每次只打印与顺序匹配的结果。
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])

1
似乎更好的处理方式是使用(排序的)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))

你可以通过在players列表推导式中按顺序列出所有球员来剥离用作sort键的lambda。或者,你可以使用operator.attrgetter('order') - Adam Smith
问题中加粗的部分指出不能使用lambda和函数。很奇怪,我知道。 - Shashank
@Shashank,我在上面的评论中解决了这个问题。这段代码中的lambda只是为了让我不必手动解析他的球员列表! :P - Adam Smith
虽然这并没有对手头的任务有太大帮助,但它确实帮助我理解lambda,所以感谢您提供的答案。 - Wes
@Wes 我认为最大的问题是这是一个XY问题!你正在寻求关于解决方案的帮助,而不是关于问题本身的帮助。你选择的解决方案是在每次迭代中对字典进行排序。这是错误的解决方案!我强烈建议构建一个对象(namedtuple 是最简单的,但你也可以定义一个类),构建一个对象列表,并且只进行一次排序。 - Adam Smith
1
@Adam 我同意你的观点,但作为一名学生,我没有能力决定解决方案的参数,而是必须遵循任务规则。我只是把这个问题发布出来,作为最后的“绝望求助”。在过去的几天里,我已经用几种不同的方法解决了这个问题,包括使用 lambda 表达式,这显然是最简洁的方法,但按照教授的要求解决问题却非常困难。 - Wes

0

虽然我来晚了,但我还是想加入这个黑客行列:

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

不过不要这样做,因为它依赖于字典项的实际但不保证的顺序。我只是为了娱乐而发布它。

另一方面,如果你移除一些玩家,它仍然可以工作,不像被接受的答案 :-P


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接