Trying to convert text to nested dict in Python - python

I am trying to get a nested dict from a list of phrases.
My phrases for example are:
show version
show module
show module 0 det
show running-config
I am expecting a structure like this:
"show":{
"version":None,
"module":{
"0": {
"det"
}
},
"running-config":None
}
What I am trying is: split the phrases, from each array I am converting it to Dict.
for line in commandsOrdered:
value = line[-1]
line.pop(-1)
for key in list(reversed(line[:])):
value = {key: value}
sL.append(value)
And once I have a list of dicts, I am merging the dictionaries.
super_dict = {}
for d in sL:
for k, v in d.items():
super_dict.setdefault(k, []).append(v)
But I am getting this:
{
"show": [
"module",
{
"module": {
"0": "det"
}
},
"running-config",
"version"
],
"0": [
"det"
],
"module": [
{
"0": "det"
}
]
}
The max depth I have is 9 words in a phrase.
Any idea how to solve this?
Thanks

Something like this is fairly straightforward:
commandsOrdered = [
'show version',
'show module',
'show module 0 det',
'show running-config'
]
result = {}
for command in commandsOrdered:
parts = command.split()
d = result
for key in parts[:-1]:
if key not in d or not d[key]:
d[key] = {}
d = d[key]
d[parts[-1]] = None
print(result)
Output:
{'show': {'version': None, 'module': {'0': {'det': None}}, 'running-config': None}}
Not using defaultdict to meet the None requirement. You could easily write this recursively as well though, given the limited depth. That would make for simpler code, but not a faster solution per se.

Related

Python Dictionary comprehension with condition

Suppose that I have a dict named data like below:
{
001: {
'data': {
'fruit': 'apple',
'vegetable': 'spinach'
},
'text': 'lorem ipsum',
'status': 10
},
002: {
.
.
.
}
}
I want to flatten(?) the data key and convert it to this:
{
001: {
'fruit': 'apple',
'vegetable': 'spinach',
'text': 'lorem ipsum',
'status': 10
},
002: {
.
.
.
}
}
I am trying to achieve this using dict comprehensions. Below implementation is with for loops:
mydict = {}
for id, values in data.items():
mydict[id] = {}
for label, value in values.items():
if label == 'data':
for x, y in value.items():
mydict[id][x] = y
else:
mydict[id][label] = value
I tried below comprehension but it gives syntax error:
mydict = {
id: {x: y} for x, y in value.items() if label == 'data' else {label: value}
for id, values in data.items() for label, value in values.items()}
Is there a way to achieve this using comprehensions only?
With dict expansions:
mydict = {i:{**v['data'], **{k:u for k, u in v.items() if k != "data"}} for i, v in data.items()}
The if clause in a comprehension (dict, list, set, generator) applies to the iteration itself, it can not be used for the production. For that you need conditionals in the production.
Generally speaking, comprehensions are really a reorganisation of a specific kind of (possibly nested) iterations:
a bunch of iterations and conditions, possibly nested
a single append/set
So
for a in b:
if c:
for d in e:
for f in g:
if h:
thing.append(i)
can be comprehension-ified, just move the production (i) to the head and put the other bits in a flat sequence:
thing = [
i
for a in b
if c
for d in e
for f in g
if h
]
Now your comprehension makes no sense, because it starts with iterating value, and there's no else in comprehension filter, and even if we add parens {x: y} for x, y in value.items() is not a value. Comprehensions also do not "merge" items, so with:
mydict = {
id: {label: value}
for id, values in data.items() for label, value in values.items()
}
Well you'll get only the last {label: value} for each id, because that's how dicts work.
Here if you consider the production loop, it's this:
for id, values in data.items():
mydict[id] = {}
This means that is your dict comprehension:
mydict = {
id: {}
for id, values in data.items()
}
the rest of the iteration is filling the value, so it needs to be a separate iteration inside the production:
mydict = {
id: {
label: value ???
for label, value in values.items()
}
for id, values in data.items()
}
In which case you hit the issue that this doesn't quite work, because you can't "conditionally iterate" in comprehensions, it's all or nothing.
Except you can: the right side of in is a normal expression, so you can do whatever you want with it, meaning you can unfold-or-refold:
mydict = {
id: {
x: y
for label, value in values.items()
for x, y in (value.items() if label == 'data' else [(label, value)])
}
for id, values in data.items()
}
This is a touch more expensive in the non-data case as you need to re-wrap the key and value in a tuple and list, but that's unlikely to be a huge deal.
An other alternative, instead of using a conditional comprehension, is to use splatting to merge the two dicts (once of which you create via a comp):
mydict = {
id: {
**values['data'],
**{label: value for label, value in values.items() if label != 'data'}
}
for id, values in data.items()
}
This can also be applied to the original to simplify it:
mydict = {}
for id, values in data.items():
mydict[id] = {}
for label, value in values.items():
if label == 'data':
mydict[id].update(value)
else:
mydict[id][label] = value
let me simplify;
sample_data = {
"001": {
"data": {
"fruit": 'apple',
"vegetable": 'spinach'
},
"text": 'lorem ipsum',
"status": 10
},
"002": {
"data": {
"fruit": 'apple',
"vegetable": 'spinach'
},
"text": 'lorem ipsum',
"status": 10
}
}
for key, row in sample_data.items():
if 'data' in row.keys():
info = sample_data[key].pop('data')
sample_data[key] = {**row, **info}
print(sample_data)

Querying a nested JSON file in Python without Indexing

I have the below Json file which I need to query to get the values of the keys inside 'validations' in a list
for example the column_values_not_null output will need to be this:
['lu_name', 'transaction_amount']
"validation_file_name": "ctm",
"connection_type": "s3",
"low_threshold": 500000,
"high_threshold": 1000000,
"frequency": "weekly",
"validations": [
{
"columns_to_match_ordered_list" :[
"lu_name",
"site_name",
"transaction_date_time",
"margin",
"transaction_currency_code",
"reversal_indicator_description",
"reversal_amount",
"original_amount"
]
},
{
"column_values_not_null":[
"lu_name",
"transaction_amount"
]
},
{
"column_values_not_duplicate": [
"lu_name",
"response_code_description"
]
}
]
I am able to do the below but I need to do this without using the index value
f = open('test.json')
json_content = json.load(f)
print(json_content['validations'][1]['column_values_not_null'])
Get a list by querying the validations key. The sum( ,[]) are used to flat the list (as required by the condition "without using the index value" if got it right), for details about it with pros and cons see doc.
data = #
def validations(data: dict, key_query: str) -> list:
for k, v in data.items():
if k == 'validations':
return sum(sum([list(d.values()) for d in v if key_query in d], []), [])
print(validations(data, query='column_values_not_null'))
# ['lu_name', 'transaction_amount']

How to combine a dict to a json file as an object with same index in Python?

The question may be confusing I know however, I don't know how to ask this properly.
Let me explain the issue. I have a json file like this:
{
"0": "MyItem",
"1": "AnotherItem"
}
Then I am generating a dictionary with the same context above. Like this.
{
"UniqueId": "52355",
"AnotherUniqueId": "234235"
}
They have same length. What I want to do is I want to parse this dictionary to this json file at the same index as an object like:
{
{"0": "MyItem", "UniqueId": "52355"}
{"1": "AnotherItem", "AnotherUniqueId": "234235"}
}
How to achieve this ?
it takes item of each dict and combines them with { **dict1, **dict2 }
then stores each dict as key-value pairs of final dicts.
n = {
"0": "MyItem",
"1": "AnotherItem"
}
m = {
"UniqueId": "52355",
"AnotherUniqueId": "234235"
}
c = {}
for i, keys in enumerate(zip(n, m)):
a, b = keys
c[i] = { **{a:n[a]} , **{b:m[b]} }
print(c)
output :
{
0: {'0': 'MyItem', 'UniqueId': '52355'},
1: {'1': 'AnotherItem', 'AnotherUniqueId': '234235'}
}
Your dictionaries in the final dictionary need to be accompanied by some sort of key since a dictionary is a key-value pair, it wouldn't make sense to not have a key for a value. The output you should go after is this for example
{
0: {"0": "MyItem", "UniqueId": "52355"},
1: {"1": "AnotherItem", "AnotherUniqueId": "234235"}
}
Here's my solution
b = {
"UniqueId": "52355",
"AnotherUniqueId": "234235"
}
a = {
"0": "MyItem",
"1": "AnotherItem"
}
# Assuming a and b are of the same length
c = {} # will contain the final dictionaries
index = 0
for i,j in zip(a,b):
temp = {}
temp[i]=a[i]
temp[j]=b[j]
c[index] = temp
index+=1
print(c)

How to add a new key value pair to existing key value pair from list of dicts?

I have a dictionary with a parent-key and its value is a dict. I want to extract a key,val pair from a list of dict.
given:
{"Premier" : {}}
I want to extract:
all_compseasons = content: [
{
label: "2019/20",
id: 274
},
{
label: "2018/19",
id: 210
}]
So to get:
{"Premier" :
{"2019/20" : 274,
"2018/19" : 210
}
}
I can't seem to find a good way to do it. I've tried below given other examples of the problem, but doesn't work.
compseasons = {}
for comp in all_compseasons:
competition_id = 'Premier'
index = competition_id
compseasons[index]comp['label'] = comp['id']
Your very close. Dictionary keys need to be referenced with surrounding [], so comp['label'] should be [comp['label']]. You can also just use the given dictionary {"Premier" : {}} instead of creating a new one with compseasons = {}, but either will give you the same result.
Working solution:
d = {"Premier": {}}
all_compseasons = [{"label": "2019/20", "id": 274}, {"label": "2018/19", "id": 210}]
for comp in all_compseasons:
d["Premier"][comp["label"]] = comp["id"]
print(d)
# {'Premier': {'2019/20': 274, '2018/19': 210}}
You just made a mistake in how you declared compseasons and how you are accessing the value of premier key which is also a dictionary.
Declaring compseasons = {"Premier" : {}} will not give you KeyError when you are trying to access it via compseasons[index] since Premier has already been inserted as a key.
Second, since your value of Premier itself is a dictionary, you should access the inner key enclosed in [] which would translate to compseasons[index][comp['label']] = comp['id'].
all_compseasons = [
{
'label': "2019/20",
'id': 274
},
{
'label': "2018/19",
'id': 210
}]
compseasons = {"Premier" : {}}
for comp in all_compseasons:
competition_id = 'Premier'
index = competition_id
compseasons[index][comp['label']] = comp['id']

How to perform quick upleveling in python?

I have the following object in python:
{
name: John,
age: {
years:18
},
computer_skills: {
years:4
},
mile_runner: {
years:2
}
}
I have an array with 100 people with the same structure.
What is the best way to go through all 100 people and make it such that there is no more "years"? In other words, each object in the 100 would look something like:
{
name: John,
age:18,
computer_skills:4,
mile_runner:2
}
I know I can do something in pseudocode:
for(item in list):
if('years' in (specific key)):
specifickey = item[(specific key)][(years)]
But is there a smarter/more efficent way?
Your pseudo-code is already pretty good I think:
for person in persons:
for k, v in person.items():
if isinstance(v, dict) and 'years' in v:
person[k] = v['years']
This overwrites every property which is a dictionary that has a years property with that property’s value.
Unlike other solutions (like dict comprehensions), this will modify the object in-place, so no new memory to keep everything is required.
def flatten(d):
ret = {}
for key, value in d.iteritems():
if isinstance(value, dict) and len(value) == 1 and "years" in value:
ret[key] = value["years"]
else:
ret[key] = value
return ret
d = {
"name": "John",
"age": {
"years":18
},
"computer_skills": {
"years":4
},
"mile_runner": {
"years":2
}
}
print flatten(d)
Result:
{'age': 18, 'mile_runner': 2, 'name': 'John', 'computer_skills': 4}
Dictionary comprehension:
import json
with open("input.json") as f:
cont = json.load(f)
print {el:cont[el]["years"] if "years" in cont[el] else cont[el] for el in cont}
prints
{u'age': 18, u'mile_runner': 2, u'name': u'John', u'computer_skills': 4}
where input.json contains
{
"name": "John",
"age": {
"years":18
},
"computer_skills": {
"years":4
},
"mile_runner": {
"years":2
}
}
Linear with regards to number of elements, you can't really hope for any lower.
As people said in the comments, it isn't exactly clear what your "object" is, but assuming that you actually have a list of dicts like this:
list = [{
'name': 'John',
'age': {
'years': 18
},
'computer_skills': {
'years':4
},
'mile_runner': {
'years':2
}
}]
Then you can do something like this:
for item in list:
for key in item:
try:
item[key] = item[key]['years']
except (TypeError, KeyError):
pass
Result:
list = [{'age': 18, 'mile_runner': 2, 'name': 'John', 'computer_skills': 4}]

Categories