for - dict() python
Crear dict de la lista de la lista (7)
Tengo un archivo de texto que leí. Este es un archivo de registro por lo que sigue un patrón particular. Necesito crear un JSON en última instancia, pero a partir de la investigación de este problema, una vez que esté en un dictado, se tratará de usar json.loads()
o json.dumps()
.
Una muestra del archivo de texto está abajo.
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 estructura de dictado que estoy buscando últimamente es
{
"INFO": {
"submit": {
"is_test": false,
"username": "Mary"
},
"env": {
"sys.platform": "linux2",
"os.name": "ubuntu"
}
}
}
Estoy ignorando la información de marca de tiempo en cada lista por ahora.
Este es un fragmento del código que estoy usando,
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}
Lo que resulta en una salida no deseada,
{''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'': {}}}}}}}}}}}}}}}}}
Necesito rellenar dinámicamente el dict porque no conozco los nombres reales de campo / clave.
Compruebe la presencia de llaves:
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)
Este es uno de los casos raros donde la recursión en Python parece ser apropiada y útil. La siguiente función agrega un value
al diccionario jerárquico d
especificado por la lista 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 función trabaja con los diccionarios de profundidad arbitraria. El resto es solo cuestión de llamar a la función:
d = {}
for line in file:
keys, value = line.split("=")
keys = keys.split(":")
add_to_dict(d, keys, value.strip())
Resultado:
{''INFO'': {''20180606_141527'': {
''submit'': {''is_test'': ''False'',
''username'': ''Mary''},
''env'': {''sys.platform'': ''linux2'',
''os.name'': ''ubuntu''}}}}
Puede modificar el código para excluir ciertos niveles (como la marca de tiempo).
Fuente :
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)
Resultado:
{''INFO'': {''submit'': {''is_test'': ''False'', ''username'': ''Mary''}, ''env'': {''sys.platform'': ''linux2'', ''os.name'': ''ubuntu''}}}
Puedes usar 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))
Salida:
{
"INFO": {
"env": {
"os.name": "ubuntu",
"sys.platform": "linux2"
},
"submit": {
"is_test": "False",
"username": "Mary"
}
}
}
Puedes usar una collections.defaultdict()
anidada.defaultdict collections.defaultdict()
aquí:
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)
Lo que da lo siguiente:
defaultdict(<function <lambda> at 0x7ff8a341aea0>,
{''INFO'': defaultdict(<class ''dict''>,
{''env'': {''os.name'': ''ubuntu'',
''sys.platform'': ''linux2''},
''submit'': {''is_test'': ''False'',
''username'': ''Mary''}})})
Nota: defaultdict()
es una subclase del dictado incorporado, por lo que no es motivo para convertirlo en dict
en el resultado final. Además, defaultdict()
también se puede serializar a JSON con json.dumps()
.
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''}}}
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)
Salida
{
''INFO'': {
''submit'': {
''is_test'': ''False'', ''username'': ''Mary''
},
''env'': {
''sys.platform'': ''linux2'', ''os.name'': ''ubuntu''
}
}
}