你可以使用标准库中的json模块来实现自定义字典键的排序顺序。

huangapple go评论77阅读模式
英文:

How can I use a custom sort order for dict keys with the standard library json module?

问题

我正在尝试将一个字典以JSON格式输出,使用的是Python 2.7(无法升级)。
data 中的键是包含数字的字符串,例如 'item_10',并且没有固定的顺序。例如,以下代码生成一些测试数据:

import random

data = {}
numbers = list(range(1, 12))
random.shuffle(numbers)
for value in numbers:
    data['item_{}'.format(value)] = 'data{}'.format(value)

我尝试使用以下方式:

print(json.dumps(data, sort_keys=True, indent=2))

但是,我希望键按自然排序排列,如下所示:

{
  "item_1": "data1",  
  "item_2": "data2",
  ...
  "item_10": "data10",
  "item_11": "data11"
}

然而,实际上,我得到的是按Python默认的排序顺序排序的键:

{
  "item_1": "data1",
  "item_10": "data10",
  "item_11": "data11",
  ...
  "item_2": "data2"
}

我该如何获得所需的结果?

英文:

I am trying to output a dictionary as JSON, using python 2.7 (this can not be upgraded)
The keys in the data are strings that contain numbers, like 'item_10', and have an arbitrary order. For example, this code generates some test data:

import random

data = {}
numbers = list(range(1, 12))
random.shuffle(numbers)
for value in numbers:
    data['item_{}'.format(value)] = 'data{}'.format(value)

I tried using:

print(json.dumps(data, sort_keys=True, indent=2))

However, I want the keys to be sorted naturally, like:

{
  "item_1": "data1",  
  "item_2": "data2",
  ...
  "item_10": "data10",
  "item_11": "data11"
}

Instead, I get keys sorted by Python's default sort order:

{
  "item_1": "data1",
  "item_10": "data10",
  "item_11": "data11",
  ...
  "item_2": "data2"
}

How can I get this result?

答案1

得分: 1

以下是您要翻译的内容:

通过使键“自然可比”

假设我们有一个实现自然排序比较的键函数,就像Claudiu在相关问题的回答中所做的那样

import re

def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
    return [int(text) if text.isdigit() else text.lower()
            for text in _nsre.split(s)]

然后,我们可以创建一个字符串的包装类,该类使用该函数进行比较,转换字典的键,并像以前一样继续进行:

from functools import total_ordering

@total_ordering
class NaturalSortingStr(str):
    def __lt__(self, other):
        return natural_sort_key(self) < natural_sort_key(other)

fixed = {NaturalSortingStr(k):v for k, v in data.items()}

print(json.dumps(fixed,sort_keys=True,indent=2))

请注意,functools.total_ordering是在Python 3.2中引入的。在旧版本中,我们应该明确定义__gt____le____ge__,以相应的方式定义。当然,基本的str__eq____ne__的实现不需要被替换。

(在2.7中,我们也可以实现一个相应的__cmp__,但这会在3.x中断。)

通过首先排序键

在3.7及以上的版本中,字典键保证保持它们的顺序;在早期版本中,我们可以使用collections.OrderedDict来获得该属性。请注意,这不会对键进行排序,但会保持插入的顺序

因此,我们可以确定键的必要顺序,然后通过按该顺序插入键来创建一个新字典:

import sys
if sys.version_info < (3, 7):
    # 在更新的版本中,这也可以工作,但是不必要
    from collections import OrderedDict as odict
else:
    odict = dict

sorted_keys = sorted(data.keys(), key=natural_sort_key)
sorted_data = odict((k, data[k]) for k in sorted_keys)
print(json.dumps(sorted_data, indent=2))

由于数据事先排序,因此sort_keys=True不再是必要的。在现代版本中,由于我们使用内置的dict,我们还可以编写一个字典理解(而不是将生成器传递给构造函数)。

英文:

By making the keys "naturally comparable"

Supposing that we have a key function that implements the natural-sort comparison, as in Claudiu's answer for the related question:

import re

def natural_sort_key(s, _nsre=re.compile(&#39;([0-9]+)&#39;)):
    return [int(text) if text.isdigit() else text.lower()
            for text in _nsre.split(s)]

Then we can create a wrapper class for strings which is compared using that function, transform the keys of the dict, and proceed as before:

from functools import total_ordering

@total_ordering
class NaturalSortingStr(str):
    def __lt__(self, other):
        return natural_sort_key(self) &lt; natural_sort_key(other)

fixed = {NaturalSortingStr(k):v for k, v in data.items()}

print(json.dumps(fixed,sort_keys=True,indent=2))

Note that functools.total_ordering is introduced in Python 3.2. In older versions, we should instead define __gt__, __le__ and __ge__ explicitly, in corresponding ways. (Python's sort algorithm should not use these, but it is a good idea to include consistent definitions for correctness.) Of course, the base str's implementations of __eq__ and __ne__ do not need to be replaced.

(In 2.7 we could also instead implement a corresponding __cmp__, but this will break in 3.x.)

By putting the keys in order first

In 3.7 and up, dictionary keys are guaranteed to preserve their order; in earlier versions, we can use collections.OrderedDict to get that property. Note that this does not sort keys, but maintains the order of insertion.

Thus, we can determine the necessary order for keys, and create a new dict by inserting keys in that order:

import sys
if sys.version_info &lt; (3, 7):
    # In newer versions this would also work, but is unnecessary
    from collections import OrderedDict as odict
else:
    odict = dict

sorted_keys = sorted(data.keys(), key=natural_sort_key)
sorted_data = odict((k, data[k]) for k in sorted_keys)
print(json.dumps(sorted_data, indent=2))

Since the data was sorted ahead of time, sort_keys=True is no longer necessary. In modern versions, since we are using the built-in dict, we could also write a dict comprehension (rather than passing a generator to the constructor).

答案2

得分: -1

以下是已翻译的内容:

使用 simplejson 而不是标准库

第三方 simplejson 库是 Python 标准库 JSON 支持的原始基础;但是,该库由原始开发人员积极维护,而标准库相对较旧。例如,在 Python 3.8 中,标准库似乎基于 simplejson 2.0.9;截至本文发布时,simplejson 的最新版本是 3.18.3。

使用最新版本的 simplejson,我们可以简单地将排序键指定为 item_sort_key

import simplejson as json
import re

# 再次使用 Claudiu 的实现
def natural_sort_key(s, _nsre=re.compile(r'([0-9]+)')):
    return [int(text) if text.isdigit() else text.lower()
            for text in _nsre.split(s)]

print(json.dumps(data, item_sort_key=natural_sort_key, indent=2))
英文:

Using simplejson instead of the standard library

The third-party simplejson library is the original basis of Python's standard library JSON support; however, it is actively maintained by the original developer, and the standard library uses very old versions, relatively speaking. For example, in Python 3.8, the standard library appears to be based on simplejson 2.0.9; as of posting, the latest version of simplejson is 3.18.3.

Using an up-to-date version of simplejson, we can simply specify the sort key as item_sort_key:

import simplejson as json
import re

# Again using Claudiu&#39;s implementation
def natural_sort_key(s, _nsre=re.compile(&#39;([0-9]+)&#39;)):
    return [int(text) if text.isdigit() else text.lower()
            for text in _nsre.split(s)]

print(json.dumps(data, item_sort_key=natural_sort_key, indent=2))

huangapple
  • 本文由 发表于 2023年2月23日 21:08:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/75545272.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定