简体   繁体   中英

How to traverse dictionary keys in sorted order

I am reading a cfg file, and receive a dictionary for each section. So, for example:

Config-File:

[General]
parameter1="Param1"
parameter2="Param2"

[FileList]
file001="file1.txt"
file002="file2.txt" ......

I have the FileList section stored in a dictionary called section . In this example, I can access "file1.txt" as test = section["file001"] , so test == "file1.txt" . To access every file of FileList one after the other, I could try the following:

for i in range(1, (number_of_files + 1)):
    access_key = str("file_00" + str(i))
    print(section[access_key])

This is my current solution, but I don't like it at all. First of all, it looks kind of messy in python, but I will also face problems when more than 9 files are listed in the config.

I could also do it like:

for i in range(1, (number_of_files + 1)):
    if (i <= 9):
        access_key = str("file_00" + str(i))
    elif (i > 9 and i < 100):
        access_key = str("file_0" + str(i))
    print(section[access_key])

But I don't want to start with that because it becomes even worse. So my question is: What would be a proper and relatively clean way to go through all the file names in order? I definitely need the loop because I need to perform some actions with every file.

Use zero padding to generate the file number (for eg see this SO question answer: https://stackoverflow.com/a/339013/3775361 ). That way you don't have to write the logic of moving through digit rollover yourself—you can use built-in Python functionality to do it for you. If you're using Python 3 I'd also recommend you try out f-strings (one of the suggested solutions at the link above). They're awesome!

If we can assume the file number has three digits, then you can do the followings to achieve zero padding. All of the below returns "015".

i = 15

str(i).zfill(3)
# or
"%03d" % i 
# or
"{:0>3}".format(i)
# or
f"{i:0>3}"

Start by looking at the keys you actually have instead of guessing what they might be. You need to filter out the ones that match your pattern, and sort according to the numerical portion.

keys = [key for key in section.keys() if key.startswith('file') and key[4:].isdigit()]

You can add additional conditions, like len(key) > 4 , or drop the conditions entirely. You might also consider learning regular expressions to make the checking more elegant.

To sort the names without having to account for padding, you can do something like

keys = sorted(keys, key=lambda s: int(s[4:]))

You can also try a library like natsort , which will handle the custom sort key much more generally.

Now you can iterate over the keys and do whatever you want:

for key in sorted((k for k in section if k.startswith('file') and k[4:].isdigit()), key=lambda s: int(s[4:])):
    print(section[key])

Here is what a solution equipt with re and natsort might look like:

import re
from natsort import natsorted

pattern = re.compile(r'file\d+')
for key in natsorted(k for k in section if pattern.fullmatch(k)):
    print(section[key])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM