从 TOML 配置中对两层嵌套字典进行排序

问题描述 投票:0回答:1

我有想法创建一个 TOML 配置文件,它定义某种要执行的作业链。这些作业不会立即执行,中间可能会有中断,因此还应该进行一些分组,以便有机会在链之间执行某些操作。

TOML 文件示例如下所示:

[jobid_3.2]
name='script_3.2'
type='sql'
[jobid_10.1]
name='whatever_works'
type='shell'
[jobid_1.1]
name='script_1.1'
type='shell'
[jobid_3.1]
name='foobar'
type='shell'
[jobid_2.1]
name='barbaz'
type='shell'
[jobid_2.2]
name='script_2.2'
type='sql'

所以,顺序有点乱,因此配置读取应该处于某种有序状态。

问题 1:将 TOML 字符串读入变量时,最终会得到嵌套字典,在本例中为 3(jobid_xxx、y,然后是名称类型的字典)
问题 2:TOML 将整数识别为字符串,而不是用作表数组时的整数。这意味着我们必须在第一个嵌套字典中进行一些转换。
问题 3:第一级排序应该发生在数字上,而不是字符串本身。当然,我可以使用

[3.2]
代替
[jobid_3.2]
,但这实际上看起来更好,更具描述性。

那么,如何解决这个问题?

python python-3.x dictionary
1个回答
0
投票

我花了一些时间才明白这是如何运作的,经历了很多尝试和失败。但是,工作解决方案如下所示:

import tomli
from collections import OrderedDict

string = """[jobid_3.2]
name='script_3.2'
type='sql'
[jobid_10.1]
name='whatever_works'
type='shell'
[jobid_1.1]
name='script_1.1'
type='shell'
[jobid_3.1]
name='foobar'
type='shell'
[jobid_2.1]
name='barbaz'
type='shell'
[jobid_2.2]
name='script_2.2'
type='sql'
"""

t = tomli.loads(string)

scripts = OrderedDict(
    sorted(
        {
            k_outer: dict(
                sorted(
                    {
                        int(k_inner): v_inner for k_inner, v_inner in v_outer.items()
                    }.items()
                )
            )
            for k_outer, v_outer in t.items()
        }.items(),
        key=lambda x: int(re.match("^.+_(\d+)", x[0]).group(1)),
    )
)

print(scripts)

输出:

OrderedDict([('jobid_1', {1: {'name': 'script_1.1', 'type': 'shell'}}),
             ('jobid_2',
              {1: {'name': 'barbaz', 'type': 'shell'},
               2: {'name': 'script_2.2', 'type': 'sql'}}),
             ('jobid_3',
              {1: {'name': 'foobar', 'type': 'shell'},
               2: {'name': 'script_3.2', 'type': 'sql'}}),
             ('jobid_10', {1: {'name': 'whatever_works', 'type': 'shell'}})])

看起来确实很复杂,我花了半天时间才弄清楚,但效果很好。

最内部的排序是针对第 2 层的字典,我们也希望将数字字符串转换为普通整数。因此我们必须从父字典(v_outer)中获取值。外面的

sorted
最终是在第一层进行整体排序。我们使用正则表达式并将匹配转换为整数,这样我们得到 1,2,3,4,..., 10 而不是 1,10,2,3,4,...

作为替代方案,您可以使用

[3.2]
作为表格标记并省略尾随字符串
jobid_
,因此您可能需要稍微更改一下代码:

scripts = OrderedDict(
    sorted(
        {
            int(k_outer): dict(
                sorted(
                    {int(k_inner): v_inner for k_inner, v_inner in v_outer.items()}.items()
                )
            ) for k_outer, v_outer in t.items()
        }.items()
    )
)

您可以将密钥放在外部

sorted
,但需要将字符串转换为整数。

如果有更简单的方法,请在这里分享,我会很感兴趣!

© www.soinside.com 2019 - 2024. All rights reserved.