web-dev-qa-db-fra.com

Créer un dict à partir d'une liste de liste

J'ai un fichier texte dans lequel j'ai lu. Il s'agit d'un fichier journal qui suit un modèle particulier. Je dois finalement créer un JSON, mais en recherchant ce problème, une fois que ce sera dans un dict, ce sera une question d’utiliser json.loads() ou json.dumps()

Un exemple du fichier texte est ci-dessous. 

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

La structure de dict que je cherche en fin de compte est 

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

J'ignore les informations d'horodatage de chaque liste pour l'instant. 

Ceci est un extrait du code que j'utilise, 

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}

Ce qui entraîne une sortie indésirable, 

{'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': {}}}}}}}}}}}}}}}}}

Je dois remplir dynamiquement le dict parce que je ne connais pas les noms de champs/clés réels. 

16
Bryce Ramgovind
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)

Sortie

{
'INFO': {
    'submit': {
        'is_test': 'False', 'username': 'Mary'
        }, 
    'env': {
        'sys.platform': 'linux2', 'os.name': 'ubuntu'
        }
    }
}  
12
Druta Ruslan

C'est l'un des rares cas où la récursion en Python semble appropriée et utile. La fonction suivante ajoute une value au dictionnaire hiérarchique d spécifié par la liste de keys:

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)

La fonction fonctionne avec les dictionnaires de profondeur arbitraire. Le reste consiste simplement à appeler la fonction:

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

Résultat:

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

Vous pouvez modifier le code pour exclure certains niveaux (tels que l'horodatage).

6
DYZ

Vous pouvez utiliser un collections.defaultdict() here:

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)

Ce qui donne ce qui suit:

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

Remarque:defaultdict() est une sous-classe de la variable intégrée dict. Il n'est donc pas nécessaire de la convertir en dict dans le résultat final. De plus, defaultdict() peut également être sérialisé en JSON avec json.dumps()

4
RoadRunner

Vous pouvez utiliser 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))

Sortie:

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

La source : 

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)

Résultat : 

 {'INFO': {'submit': {'is_test': 'False', 'username': 'Mary'}, 'env': {'sys.platform': 'linux2', 'os.name': 'ubuntu'}}}
0
clem
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'}}}
0
Sunitha

Vérifier la présence de clés:

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)
0
ted