使用不一致的日期时间格式对 JSON 个词典进行排序

Sort JSON dictionaries using datetime format not consistent

我有 JSON 文件(post 来自 API 的响应)- 我需要按某个键对字典进行排序以便解析 JSON 文件按年代顺序。研究数据后,我可以按元数据中的日期格式或S5CV[0156]P0.xml

的数字序列排序

您可以在此处 JSON 中加载一个文本示例 - http://pastebin.com/0NS5BiDk

我写了 2 个代码来按某个键对对象列表进行排序。第一个按 xml 的 'text' 排序。第二个来自 [metadata][0][value].

第一个可行,但一些 XML,即使它们的数量更多,实际上包含的文档比我预期的要旧。

对于第二个代码,日期格式不一致,有时根本不存在该值。我正在努力以一致的方式提取日期时间格式。第二个也给我一个错误,但我不明白为什么 - 字符串索引必须是整数。

# 1st code (it works but not ideal)

# load post response r1 in json (python 3.5)
j=r1.json() 

# iterate through dictionaries and sort by the 4 num of xml (ex. 0156)

list = []
for row in j["tree"]["children"][0]["children"]:
    list.append(row)

newlist = sorted(list, key=lambda k: k['text'][-9:])
print(newlist)


# 2nd code. I need something to make consistent datetime,
# except missing values and solve the list index error

list = []
for row in j["tree"]["children"][0]["children"]:
    list.append(row)

# extract the last 3 blocks of characters from the [metadata][0][value]
# usually are like this "7th april, 1922." and trasform in datatime format
# using dparser.parse 

def date(key):
    return dparser.parse((' '.join(key.split(' ')[-3:])),fuzzy=True)

def order(slist):
    try:
        return sorted(slist, key=lambda k: k[date(["metadata"][0]["value"])])

    except ValueError:
        return 0

print(order(list))


#update
    orig_list = j["tree"]["children"][0]["children"]


    cleaned_list = sorted((x for x in orig_list if extract_date(x) != DEFAULT_DATE),
                          key=extract_date)    


    first_date = extract_date(cleaned_list[0])
    if first_date != DEFAULT_DATE:  # valid date found?
            cleaned_list [0] ['date'] = first_date
    print(first_date)

    middle_date = extract_date(cleaned_list[len(cleaned_list)//2])
    if middle_date != DEFAULT_DATE:  # valid date found?
            cleaned_list [0] ['date'] = middle_date
    print(middle_date)

    last_date = extract_date(cleaned_list [-1])
    if last_date != DEFAULT_DATE:  # valid date found?
            cleaned_list [0] ['date'] = last_date
    print(last_date)      

显然,如果数据不可靠,您不能使用 .xml 文件名对数据进行排序,因此最有前途的策略似乎是您在第二个代码中尝试执行的操作。

当我提到需要一个日期时间来对我对你的其他问题的评论中的项目进行排序时,我的字面意思是像 datetime.date 实例,而不是像 "28th july, 1933" 这样的字符串,它不会提供需要正确排序,因为它们将按字典顺序相互比较,而不是像 datetime.dates.

那样在数字上进行比较

这似乎有效。它使用 re 模块在通常包含它们的字符串中搜索日期模式(那些具有与值 "Comprising period from" 关联的 "name" 的字符串)。如果字符串中有多个日期匹配,则使用最后一个。然后将其转换为 date 实例并作为键值返回。

由于某些项目没有有效的日期字符串,因此将使用默认日期字符串进行排序。在下面的代码中,一个最早的有效日期被用作默认值——这使得所有有日期问题的项目都出现在排序列表的开头。它们后面的任何项目都应按正确的顺序排列。

不确定您应该如何处理缺少日期信息的项目 - 如果没有日期信息,您唯一的选择是猜测一个值,忽略它们,或者将其视为错误。

# v3.2.1
import datetime
import json
import re

# default date when one isn't found
DEFAULT_DATE = datetime.date(1, 1, datetime.MINYEAR)  # 01/01/0001
MONTHS = ('january february march april may june july august september october'
          ' november december'.split())
# dictionary to map month names to numeric values 1-12
MONTH_TO_ORDINAL = dict( zip(MONTHS, range(1, 13)) )
DMY_DATE_REGEX = (r'(3[01]|[12][0-9]|[1-9])\s*(?:st|nd|rd|th)?\s*'
                + r'(' + '|'.join(MONTHS) + ')(?:[,.])*\s*'
                + r'([0-9]{4})')
MDY_DATE_REGEX = (r'(' + '|'.join(MONTHS) + ')\s+'
                + r'(3[01]|[12][0-9]|[1-9])\s*(?:st|nd|rd|th)?,\s*'
                + r'([0-9]{4})')
DMY_DATE = re.compile(DMY_DATE_REGEX, re.IGNORECASE)
MDY_DATE = re.compile(MDY_DATE_REGEX, re.IGNORECASE)

def extract_date(item):
    metadata0 = item["metadata"][0]  # check only first item in metadata list
    if metadata0.get("name") != "Comprising period from":
        return DEFAULT_DATE
    else:
        value = metadata0.get("value", "")
        matches = DMY_DATE.findall(value)  # try dmy pattern (most common)
        if matches:
            day, month, year = matches[-1]  # use last match if more than one
        else:
            matches = MDY_DATE.findall(value)  # try mdy pattern...
            if matches:
                month, day, year = matches[-1]  # use last match if more than one
            else:
                print('warning: date patterns not found in "{}"'.format(value))
                return DEFAULT_DATE

        # convert strings found into numerical values
        year, month, day = int(year), MONTH_TO_ORDINAL[month.lower()], int(day)
        return datetime.date(year, month, day)

# test files: 'json_sample.txt', 'india_congress.txt', 'olympic_games.txt'
with open('json_sample.txt', 'r') as f:
    j = json.load(f)

orig_list = j["tree"]["children"][0]["children"]
sorted_list = sorted(orig_list, key=extract_date)
for item in sorted_list:
    print(json.dumps(item, indent=4))

要回答您最新的后续问题,您可以使用 extract_date()generator expression 中预先过滤掉列表中没有可识别日期的所有项目像这样:

# to obtain a list containing only entries with a parsable date
cleaned_list = sorted((x for x in orig_list if extract_date(x) != DEFAULT_DATE),
                      key=extract_date)

一旦你有了一个所有都有有效日期的项目排序列表,你可以做如下的事情,再次使用 extract_date() 函数:

# extract and display dates of items in cleaned list
print('first date: {}'.format(extract_date(cleaned_list[0])))
print('middle date: {}'.format(extract_date(cleaned_list[len(cleaned_list)//2])))
print('last date: {}'.format(extract_date(cleaned_list[-1])))

对同一项目多次调用 extract_date() 效率有点低。为避免这种情况,您可以轻松地将 datetime.date 值 returns 即时添加到对象中,因为它是一个字典,然后只需根据需要经常引用它,而额外开销很少:

# add extracted datetime.date entry to a list item[i] if a valid one was found
date = extract_date(some_list[i])
if date != DEFAULT_DATE:  # valid date found?
    some_list[i]['date'] = date  # save by adding it to object

这通过将提取的日期存储在项目本身中来有效地缓存它。之后, datetime.date 值可以简单地用 some_list[i]['date'].

引用

作为具体示例,请考虑显示第一个、中间和最后一个对象的日期的修改示例:

# display dates of items in cleaned list
print('first date: {}'.format(cleaned_list[0]['date']))
middle = len(cleaned_list)//2
print('middle date: {}'.format(cleaned_list[middle]['date']))
print('last date: {}'.format(cleaned_list[-1]['date']))