从列表列表创建字典

Posted

技术标签:

【中文标题】从列表列表创建字典【英文标题】:Create dict from list of list 【发布时间】:2018-12-17 21:33:41 【问题描述】:

我有一个我读入的文本文件。这是一个日志文件,因此它遵循特定的模式。我最终需要创建一个 JSON,但是通过研究这个问题,一旦它在一个 dict 中,就可以使用json.loads()json.dumps()

文本文件的示例如下。

INFO:20180606_141527:submit:is_test=False
INFO:20180606_141527:submit:username=Mary
INFO:20180606_141527:env:sys.platform=linux2
INFO:20180606_141527:env:os.name=ubuntu

我最终要寻找的 dict 结构是


  "INFO": 
    "submit": 
      "is_test": false,
      "username": "Mary"
    ,
    "env": 
      "sys.platform": "linux2",
      "os.name": "ubuntu"
    
  

我现在忽略每个列表中的时间戳信息。

这是我正在使用的代码的 sn-p,

import csv
tree_dict = 
with open('file.log') as file:
    for row in file:
        for key in reversed(row.split(":")):
            tree_dict = key: tree_dict

这会导致不希望的输出,

'INFO': '20180606_141527': 'submit': 'os.name=posix\n': 'INFO': '20180606_141527': 'submit': 'sys.platform=linux2\n': 'INFO': '20180606_141527': 'submit': 'username=a227874\n': 'INFO': '20180606_141527': 'submit': 'is_test=False\n': 

我需要动态填充字典,因为我不知道实际的字段/键名。

【问题讨论】:

你想如何处理多个日志/字典?给我们一个至少有 2 个日志的例子 问题似乎与标题无关 【参考方案1】:

你可以使用itertools.groupby:

import itertools, re
content = [re.split('\=|:', i.strip('\n')) for i in open('filename.txt')]
new_content = [[a, *c] for a, _, *c in content]
def group_vals(d):
  new_d = [[a, [c for _, *c in b]] for a, b in itertools.groupby(sorted(d, key=lambda x:x[0]), key=lambda x:x[0])]
  return a:b[0][0] if len(b) ==1 else group_vals(b) for a, b in new_d

import json
print(json.dumps(group_vals(new_content), indent=4))

输出:


 "INFO": 
     "env": 
        "os.name": "ubuntu",
        "sys.platform": "linux2"
     ,
     "submit": 
         "is_test": "False",
         "username": "Mary"
     
  

【讨论】:

【参考方案2】:
with open('demo.txt') as f:
    lines = f.readlines()

dct = 

for line in lines:
    # param1 == INFO
    # param2 == submit or env
    # params3 == is_test=False etc.
    param1, _, param2, params3 = line.strip().split(':')

    # create dct[param1] =  if it is not created
    dct.setdefault(param1, )

    # create dct[param1][param2] =  if it is no created
    dct[param1].setdefault(param2, )

    # for example params3 == is_test=False
    # split it by '=' and now we unpack it
    # k == is_test
    # v == False
    k, v = params3.split('=')

    # and update our `dict` with the new values
    dct[param1][param2].update(k: v)

print(dct)

输出


'INFO': 
    'submit': 
        'is_test': 'False', 'username': 'Mary'
        , 
    'env': 
        'sys.platform': 'linux2', 'os.name': 'ubuntu'
        
    
  

【讨论】:

您可以使用setdefault 去掉两个if 检查。【参考方案3】:

您可以在这里使用嵌套的collections.defaultdict()

from collections import defaultdict
from pprint import pprint

d = defaultdict(lambda: defaultdict(dict))
with open('sample.txt') as in_file:
    for line in in_file:
        info, _, category, pair = line.strip().split(':')
        props, value = pair.split('=')
        d[info][category][props] = value

pprint(d)

这给出了以下内容:

defaultdict(<function <lambda> at 0x7ff8a341aea0>,
            'INFO': defaultdict(<class 'dict'>,
                                 'env': 'os.name': 'ubuntu',
                                          'sys.platform': 'linux2',
                                  'submit': 'is_test': 'False',
                                             'username': 'Mary'))

注意: defaultdict() 是内置 dict 的子类,因此最终结果不是将其转换为 dict 的理由。此外,defaultdict() 也可以使用json.dumps() 序列化为 JSON。

【讨论】:

您可能会补充说,出于所有实际目的,defaultdict 的行为类似于普通的dict,特别是还可以序列化为 JSON。【参考方案4】:

来源:

import os

with open('file.log') as file:
    tree_dict = 
    is_test = False
    username = ""              
    sysplatform = ""
    osname = ""
    for row in file: 
        row = row.rstrip('\n')
        for key in reversed(row.split(":")):            
            if not key.find('is_test'):
                is_test = key.split('=')[1]
            elif not key.find('username'):
                username =key.split('=')[1]
            elif not key.find('sys.platform'):
                sysplatform = key.split('=')[1]
            elif not key.find('os.name'):
                osname = key.split('=')[1]    

     tree_dict = 
         "INFO": 
              "submit": 
                       "is_test": is_test,
                        "username": username
              ,
              "env": 
                      "sys.platform":  sysplatform,
                      "os.name": osname
             
        
    
    print(tree_dict)

结果:

 'INFO': 'submit': 'is_test': 'False', 'username': 'Mary', 'env': 'sys.platform': 'linux2', 'os.name': 'ubuntu'

【讨论】:

【参考方案5】:
import re
from functools import reduce

with open('file.txt') as f:
    lines = f.readlines()

def rec_merge(d1, d2):
    for k, v in d1.items():
        if k in d2:
            d2[k] = rec_merge(v, d2[k])
    d3 = d1.copy()
    d3.update(d2)
    return d3

lst_of_tup = re.findall(r'^([^:]*):[\d_]+:([^:]*):([^=]*)=(.*)$', lines, re.MULTILINE)
lst_of_dct = [reduce(lambda x,y: y:x, reversed(t)) for t in lst_of_tup]

dct = reduce(rec_merge, lst_of_dct)

pprint(dct)
# 'INFO': 'env': 'os.name': 'ubuntu', 'sys.platform': 'linux2',
#           'submit': 'is_test': 'False', 'username': 'Mary'

【讨论】:

【参考方案6】:

这是 Python 中递归似乎合适且有用的罕见情况之一。以下函数将value 添加到keys 列表指定的分层字典d

def add_to_dict(d, keys, value): 
    if len(keys) == 1: # The last key
        d[keys[0]] = value
        return
    if keys[0] not in d:
        d[keys[0]] =  # Create a new subdict
    add_to_dict(d[keys[0]], keys[1:], value)

该函数适用于任意深度的字典。剩下的就是调用函数的事情了:

d = 
for line in file:
    keys, value = line.split("=")
    keys = keys.split(":")
    add_to_dict(d, keys, value.strip())

结果:

'INFO': '20180606_141527': 
                       'submit': 'is_test': 'False', 
                                  'username': 'Mary', 
                       'env': 'sys.platform': 'linux2', 
                               'os.name': 'ubuntu'

您可以修改代码以排除某些级别(如时间戳)。

【讨论】:

【参考方案7】:

检查是否存在密钥:

import csv
import json

tree_dict = 
with open('file.log') as file:
    tree_dict = 
    for row in file:
        keys = row.split(":")

        if keys[0] not in tree_dict:
            tree_dict[keys[0]] = 

        if keys[-2] not in tree_dict[keys[0]]:
            tree_dict[keys[0]][keys[-2]] = 

        key, value = keys[-1].split("=")

        if value == "False":
            value = False
        if value == "True":
            value = True

        tree_dict[keys[0]][keys[-2]][key] = value

dumped = json.dumps(tree_dict)

【讨论】:

以上是关于从列表列表创建字典的主要内容,如果未能解决你的问题,请参考以下文章

使用字典 get() 作为函数从地图创建 Python 列表与使用 for 循环创建字典 get() 列表

从列表列表创建字典

从 Python 列表创建字典数组

从嵌套列表创建字典 [重复]

从 Python 中的 csv 创建字典中的字典列表

从具有可变长度的列表字典创建数据框