I am trying to process my api result string the result string format is given below. My goal is to add these values to a list where each element of list will be a dictionary.
result_str = '['{"abc.xyz": "80983098429842","dev.uvw": 898420920},' \
'{"abc.xyz": "80983098429843","dev.uvw": 898420921},' \
'{"abc.xyz": "80983098429844","dev.uvw": 898420922}]'
However my code is returning a list that has only last element multiple times. Rather than having each element once.
Here is my code:
import json
def format_api_value(result_str, split_char, label_map):
results = json.loads(result_str)
d = dict()
output = []
for item in results:
output.append(d)
print(f"clearing d after appending {d} \n")
d.clear()
for k, v in item.items():
if split_char in k:
key = k.split(split_char)[len(k.split(split_char))-1]
if key in label_map:
key = label_map[key]
d[key] = v
else:
d[k] = v
print(f"printing output intermediate {output}")
print(f"returning final list output")
print(output)
return d
if __name__ == "__main__":
result_str = '[' \
'{"abc.xyz": "80983098429842","dev.uvw": 898420920},' \
'{"abc.xyz": "80983098429843","dev.uvw": 898420921},' \
'{"abc.xyz": "80983098429844","dev.uvw": 898420922}]'
split_char = "."
label_map = {"xyz": "xyz_1", "uvw": "uvw_1"}
format_api_value(result_str, split_char, label_map)
Expected Output:
[{'xyz_1': '80983098429842', 'uvw_1': 898420920}, {'xyz_1': '80983098429843', 'uvw_1': 898420921}, {'xyz_1': '80983098429844', 'uvw_1': 898420922}]
Current Output:
[{'xyz_1': '80983098429844', 'uvw_1': 898420922}, {'xyz_1': '80983098429844', 'uvw_1': 898420922}, {'xyz_1': '80983098429844', 'uvw_1': 898420922}]
All you need is just cutting prefixes off the keys (before . inclusive):
result_str = '[{"abc.xyz": "80983098429842","dev.uvw": 898420920},' \
'{"abc.xyz": "80983098429843","dev.uvw": 898420921},{"abc.xyz": "80983098429844","dev.uvw": 898420922}]'
data = json.loads(result_str)
label_map = {"xyz": "xyz_1", "uvw": "uvw_1"}
data = [{label_map[k[k.find('.') + 1:]]:v for k, v in d.items()} for d in data]
print(data)
alternatively k[k.find('.') + 1:] can also be replaced with k.split('.')[1]
[{'xyz_1': '80983098429842', 'uvw_1': 898420920}, {'xyz_1': '80983098429843', 'uvw_1': 898420921}, {'xyz_1': '80983098429844', 'uvw_1': 898420922}]
Related
testlist = ["13A", "13B", "13C", "23D", "5D", "9B", "9C", "9D"]
What I want the list to be:
["13A-C", "23D", "5D", "9B-D"]
Bonus points if you can sort it (5,9,13,23).
For those interested, this is my current WIP script:
testlist = ["13A", "13B", "13C", "23D", "5D", "9B", "9C", "9D"]
newlist = []
lenlist = len(testlist)
for i in range(lenlist):
#check values of first
indexnum = testlist[i][:-1]
indexchar = testlist[i][-1]
if i == 0:
newlist.append(testlist[i])
if indexnum == (testlist[i-1][:-1]):
newlistvalue = (indexnum + (testlist[i-1][-1]) + "-" + (testlist[i][-1]))
if ((indexchar == "B") and ((testlist[i-1][-1]) == "A")) or ((indexchar == "D") and ((testlist[i-1][-1]) == "C")):
newlist.append(newlistvalue)
lastval = newlist[len(newlist)-1][-1]
lastval2 = newlist[(len(newlist)-2)]
#print(lastval2)
if (indexchar == "C") and (lastval == "B"):
newlistvalue = lastval2[:-1] + indexchar
#print(newlistvalue)
newlist.pop()
newlist.pop()
#print(newlistvalue)
newlist.append(newlistvalue)
else:
newlist.append(testlist[i])
print (newlist)
#print (newlistvalue)
First you'd need to create a dict of the numbers and letters, I assume there will only be one letter in each string. Then you need to sort it and format it. You can use the following:
pairs = defaultdict(list)
for s in testlist:
pairs[s[:-1]].append(s[-1])
result = [f'{k}{"-".join(dict.fromkeys([v[0], v[-1]]))}'
for k, v in sorted(pairs.items(), key=lambda x: int(x[0]))]
['5D', '9B-D', '13A-C', '23D']
On the assumption that each string in the list ends with exactly one letter, you could do this:
import re
testlist = ["13A", "13C", "13B", "23D", "5D", "9B", "9C", "9D"]
def seq(lst):
return lst[0] if len(lst) == 1 else f'{lst[0]}-{lst[-1]}'
def key(e):
return int(re.search('\d+', e)[0])
d = {}
for e in testlist:
d.setdefault(int(e[:-1]), []).append(e[-1])
print(sorted([f'{k}{seq(sorted(v))}' for k, v in d.items()], key=key))
Output:
['5D', '9B-D', '13A-B', '23D']
Note:
Subtle change to OP's data to show that this code can handle out-of-sequence values
Team,
my last string ml31 that is in log file is being skipped from getting evaluated and final resultant dictionary is missing its entry. any hint?
ml1
/core
/home
ml2
/var
/home
/lib
cpuml3
/home
/root
/raid
ml31
/home
/root
/raid
import os
homedir=os.environ.get('HOME')
print(homedir)
key_str = "ml"
val_list = []
key = ''
my_dict = {}
with open(homedir + '/backup/file2dict.result') as file2dict:
for line in file2dict:
words = line.split()
for aWord in words:
if key_str in aWord:
if key:
my_dict[key] = val_list
print(my_dict)
val_list = []
key = aWord
else:
key = aWord
else:
val_list.append(aWord)
print(my_dict)
output
{'ml1': ['/core', '/home'], 'ml2': ['/var', '/home', '/lib'], 'cpuml3': ['/home', '/root', '/raid']}
expected
{'ml1': ['/core', '/home'], 'ml2': ['/var', '/home', '/lib'], 'cpuml3': ['/home', '/root', '/raid'], 'ml31': ['/home', '/root', '/raid'] }
You assign the list to the key in the dict my_dict[key] = val_list when you reach a new key, so it doesn't come up for the last one, you need to add it at the end too
with open(homedir + '/backup/file2dict.result') as file2dict:
for line in file2dict:
words = line.split()
for aWord in words:
if key_str in aWord:
if key:
my_dict[key] = val_list
val_list = []
key = aWord
else:
key = aWord
else:
val_list.append(aWord)
my_dict[key] = val_list
You can improve it with collections.defaultdict
key = ''
key_str = "ml"
my_dict = defaultdict(list)
# from pathlib import Path
content = Path(homedir + '/backup/file2dict.result').read_text().splitlines()
for word in content:
if key_str in word:
key = word
elif word: # ensure not use empty lines
my_dict[key].append(word)
I want to do something with column data which is a list. like:
inputs:
col-A
[{'name':'1','age':'12'}, {'name':'2','age':'12'}]
[{'name':'3','age':'18'}, {'name':'7','age':'15'}]
....
outputs:
col-A
[{'1-age':'12'}, {'2-age':'12'}]
[{'3-age':'18'}, {'7-age':'15'}]
....
My code is:
def deal(dict_col, prefix_key):
key_value = dict_col[prefix_key]+'-'
dict_col.pop(prefix_key, None)
items = copy.deepcopy(dict_col)
for key, value in items.items():
dict_col[key_value+key] = dict_col.pop(key)
return dict_col
prefix = "name"
[[deal(sub_item, prefix) for sub_item in item] for item in df[col-A]]
Some items will be processed multiple times.
Because the return value of deal method will be swapped to item in real time?
For example:
For deal method we
input:
{'name':'1','age':'12'}
output:
{'1-age':'12'}
Then the next input may be {'1-age':'12'} , and now we have no name or age to deal with.
How to solve this problem?
You can use the pandas apply method for it here some code:
import pandas as pd
d = {'col-A' : [[{'name' : '1', 'age': '12'}, {'name' : '2', 'age': '12'}],[{'name' : '3', 'age': '18'},{'name' : '7', 'age': '15'}]]}
df = pd.DataFrame(d)
def deal(row, prefix):
out_list = []
for sub_dict in row:
out_dict = {}
out_str = sub_dict.get(prefix) + '-'
for k,v in sub_dict.items():
out_dict[out_str + k] = v
out_list.append(out_dict)
return out_list
prefix = 'name'
df['col-A'] = df['col-A'].apply(lambda x : deal(x, prefix))
print(df)
You could push some of the code in a one-liner if you like that more:
def deal(row, prefix):
out_list = []
for sub_dict in row:
out_dict = dict((sub_dict[prefix] + '-' + k , sub_dict[k]) for k in sub_dict.keys() if k != prefix)
out_list.append(out_dict)
return out_list
prefix = 'name'
df['col-A'] = df['col-A'].apply(lambda x : deal(x, prefix)
Just for the fun of it you could even bring it down to one single line (not recommended due to poor readability:
prefix = "name"
df['col-A'] = df['col-A'].apply(lambda row : [dict((sub_dict[prefix] + '-' + k , sub_dict[k]) for k in sub_dict.keys() if k != prefix) for sub_dict in row])
I believe you need .get function for select with default value if not exist key in dict:
def deal(dict_col, prefix_key):
key_value = dict_col.get(prefix_key, 'not_exist')+'-'
dict_col.pop(prefix_key, None)
items = copy.deepcopy(dict_col)
for key, value in items.items():
dict_col[key_value+key] = dict_col.pop(key)
return dict_col
so i have the following data:
Apples = 1
Bananas = 1
Box_Cashew =
{
Cashew = 1
}
Dragonsfruit = 2
Crate_box_epox=
{
box_epox =
{
epox = 2
}
}
and want to make a Dictionary from this txt, as it follows:
{'Apple':'1' , 'Bananas' : '1' , 'Box_Cashew' : {'Cashew':'1'} , 'Dragonsfruit' : '2', 'Crate_box_epox' : { 'box_epox' : {'epox':2}}}
i tried read line by line with the code below, but i dont know what to do when i got a dict within a dict.
edit:
#PrestonM and #juanpa.arrivillaga
The text file:
unit=9023
state=1411
flags=
{
1NobelChemistry=yes
1NobelLiterature=yes
1NobelMedicine=yes
}
worldmarket=
{
worldmarket_pool=
{
ammunition=204.50766
}
}
The code:
text_file = open("teste.v2", "r")
lines = text_file.readlines()
d={}
for line in lines:
try:
(key1, val) = line.replace('\t','').replace('\n','').split('=')
d[str(key1)] = val
except:
pass
result:
>>>d
{'unit':'9023' , 'state':'1411' , 'flags':{},'1NobelChemistry':'yes' , '1NobelLiterature':'yes' , '1NobelMedicine':'yes','worldmarket':{},'worldmarket_pool':{},'ammunition':'204.50766'}
desired result:
>>>d
{'unit':'9023' , 'state':'1411' , 'flags':{ '1NobelChemistry':'yes' , '1NobelLiterature':'yes' , '1NobelMedicine':'yes'},'worldmarket':{'worldmarket_pool':{'ammunition':'204.50766'}}}
The following seems to work in my tests. I hope the comments and text in the exceptions makes it clear what's being done.
In your code, you're simply adding everything to the same dictionary, which cannot produce the result you're after. As soon as { is encountered, you want to start adding key/value pairs to a new dictionary, that's actually stored in the old dictionary. To accomplish this, the code below keeps track of these dictionaries in a list, adding one if necessary, and removing one from the list to get back to the previous dictionary.
dictStack = [ { } ]
currentKey = None
for l in lines:
l = l.strip() # Remove whitespace at start/end
if not l: # skip empty line
continue
if l == "{":
if currentKey is None:
raise Exception("Current key not set!")
newDict = { }
dictStack[0][currentKey] = newDict
dictStack.insert(0, newDict)
currentKey = None
elif l == "}":
if currentKey is not None:
raise Exception("Current key is set, expecting {")
if len(dictStack) == 1:
raise Exception("Can't remove the final dict, there seems to be an extra '}'")
dictStack.pop(0)
else:
if currentKey is not None:
raise Exception("Current key is set, expecting {")
if not "=" in l:
raise Exception("Expecting '=' in '{}'".format(l))
key, value = l.split("=")
key, value = key.strip(), value.strip() # remove whitespace
if not value:
currentKey = key
else:
dictStack[0][key] = value
if len(dictStack) != 1:
raise Exception("Still more than one dict in the stack")
result = dictStack[0]
Here is my solution which uses recursion:
import re
def text2dict(text):
def f(ls, i):
d = {}
while i < len(ls):
if ls[i]=="}":
return d, i
m = re.match(r"(.*)=(.*)", ls[i])
k = m.group(1).strip()
v = m.group(2).strip()
if not len(v):
v, i = f(ls, i+2)
d[k] = v
i += 1
return d
return f([l.strip() for l in text.split("\n")], 0)
with open("file.txt") as f:
text = f.read()
print(text2dict(text))
def make_dict(text):
l = "{"
t = text.splitlines()
for j,i in enumerate(t):
if i != '':
line = i.replace(" ", "").split('=')
next = t[j + 1].replace(" ", "").split('=')[0] if len(t) > (j + 1) else "}"
if line[0] == "{" or line[0] == "}":
l += line[0]
else:
l += ("'"+line[0] + "':" + ("'" + line[1] + "'" + ("," if next != "}" else "") + "" if line[1] != '' else ""))
l += "}"
print(l)
make_dict(text)
Result:
{'unit':'9023','state':'1411','flags':{'1NobelChemistry':'yes','1NobelLiterature':'yes','1NobelMedicine':'yes'}'worldmarket':{'worldmarket_pool':{'ammunition':'204.50766'}}}
I have a python dictionary and a dictionary with in some of the values. I'm trying to generate a dotted delimited string of the keys in the structure with the value at the end. With the example below I'd want FIELD0 1 and NAME. I could create a for loop to process the data or a recursive function. I didn't know if there was something prebuilt method for collapsing a multilevel dictionary to delimited strings?
I was trying the following but as you know it will just append the sub dictionaries.
'.'.join('%s %s\n' % i for i in a.items())
{'BOGUS1': 'BOGUS_VAL1',
'BOGUS2': 'BOGUS_VAL1',
'FIELD0': {'F0_VAL1': 1, 'F0_VAL2': 2},
'FIELD1': {'F1_VAL1': 80, 'F1_VAL2': 67, 'F1_VAL3': 100},
'FOOBAR1': 'FB_VAL1',
'NAME': 'VALUE'}
BOGUS2.BOGUS_VAL1
.NAME.VALUE
.BOGUS1.BOGUS_VAL1
.FIELD0.{'F0_VAL1': 1, 'F0_VAL2': 2}
.FIELD1.{'F1_VAL2': 67, 'F1_VAL3': 100, 'F1_VAL1': 80}
.FOOBAR1.FB_VAL1
# Wanted results
FIELD0.F0_VAL1 1
FIELD0.F0_VAL2 2
FIELD1.F1_VAL1 80
FIELD1.F2_VAL1 67
FIELD1.F3_VAL1 100
NAME VALUE
How about something like this:
def dotnotation(d, prefix = ''):
for k, v in d.items():
if type(v) == type(dict()):
dotnotation(v, prefix + str(k) + '.')
else:
print prefix + str(k) + ' = ' + str(v)
Also the formatting can be changed according to the stored types. This should work with your example.
Here is my approach:
def dotted_keys(dic):
""" Generated dot notation keys from a dictionary """
queue = [(None, dic)] # A queue of (prefix, object)
while queue:
prefix, current = queue.pop(0)
for k, v in current.iteritems():
if isinstance(v, dict):
queue.append((k, v))
elif prefix:
yield prefix + '.' + k
else:
yield k
def dict_search(dic, dotted_key, default=None):
""" Take a dictionary and a dotted key and return the value. If not
found, return the value specified by the default parameter.
Example: dict_search(d, 'FIELD0.F0_VAL2')
"""
current = dic
keys = dotted_key.split('.')
for k in keys:
if k in current:
current = current[k]
else:
return default
return current
if __name__ == '__main__':
d = {
'BOGUS1': 'BOGUS_VAL1',
'BOGUS2': 'BOGUS_VAL1',
'FIELD0': {'F0_VAL1': 1, 'F0_VAL2': 2, 'XYZ': {'X1': 9}},
'FIELD1': {'F1_VAL1': 80, 'F1_VAL2': 67, 'F1_VAL3': 100},
'FOOBAR1': 'FB_VAL1',
'NAME': 'VALUE'
}
for k in dotted_keys(d):
print(k, '=', dict_search(d, k))
Output:
BOGUS2 = BOGUS_VAL1
NAME = VALUE
BOGUS1 = BOGUS_VAL1
FOOBAR1 = FB_VAL1
FIELD0.F0_VAL1 = 1
FIELD0.F0_VAL2 = 2
FIELD1.F1_VAL2 = 67
FIELD1.F1_VAL3 = 100
FIELD1.F1_VAL1 = 80
XYZ.X1 = None
The dotted_keys function generates a list of keys in dotted notation while the dict_search function takes a dotted key and return a value.