How to apply a function to a sequence of dicts? - python

I have the following function:
def count_chars(e):
return len(e)
I am iterating a json as follows:
In:
a_lis = []
with open('../JSON_FILE.json','r') as fa:
a = json.load(fa)
for e in a['entries']:
pprint(e)
Out:
{'data': ['string'], 'type': 'one'}
{'data': ['a string '], 'type': 'one'}
{'data': ['another string'], 'type': 'three'}
...
{'data': ['one more string'], 'type': 'two'}
How can I apply count_chars function and add it or update it as a new string in the 'data' list? For instance the expected output would look like:
{'data': ['string','6'], 'type': 'one'}
{'data': ['a string','8'], 'type': 'one'}
{'data': ['another string','14'], 'type': 'three'}
...
{'data': ['one more string','15'], 'type': 'two'}
UPDATE:
I found that my lists have more than one item, for example: ['first', 'second string']? How can I return ['first', len_1, 'second string', len_2]

You could use append():
lst = [
{"data": ["string"], "type": "one"},
{"data": ["a string "], "type": "one"},
{"data": ["another string"], "type": "three"},
]
def count_chars(e):
return len(e)
for d in lst:
d["data"].append(count_chars(d["data"][0]))
print(lst)
# [{'data': ['string', 6], 'type': 'one'}, {'data': ['a string ', 9], 'type': 'one'}, {'data': ['another string', 14], 'type': 'three'}]
If you have more strings in the list, you can use extend() and rebuild a new list:
lst = [
{"data": ["string", "hi"], "type": "one"},
{"data": ["a string "], "type": "one"},
{"data": ["another string"], "type": "three"},
]
def count_chars(e):
return len(e)
for d in lst:
newlst = []
for x in d["data"]:
newlst.extend([x, count_chars(x)])
d["data"] = newlst
print(lst)
# [{'data': ['string', 6, 'hi', 2], 'type': 'one'}, {'data': ['a string ', 9], 'type': 'one'}, {'data': ['another string', 14], 'type': 'three'}]
Note: Since count_chars() simply returns len(), it might be easier to just call len() itself.

That should work :)
def count_chars(e):
return len(e)
a_lis = []
with open('../JSON_FILE.json','r') as fa:
a = json.load(fa)
for e in a['entries']:
for String in e["data"]: # Grab one string inside the strings list.
if type(String) == int:
continue # Skip the count chars value that you appended.
Length = count_chars(String) # Apply the function.
e["data"].append(Length) # Append the returned value to the data list containing the string.
# Now we reorder the list from ["a", "ab", "abc", 1, 2, 3] to ["a", 1, "ab", 2, "abc", 3]
strings_found = int(len(e["data"])/2)
reordered_list = []
for start in range(0, strings):
reordered_list = reordered_list + [x for x in e["data"][start::strings_found ]]
e["data"] = reordered_list

Related

Converting incoming data in the form of "multipart/form-data" into a Querydict object

I want to convert a data like below to QueryDict object. Is there a ready-made class that can do this job? I could not parse the data that came in the form of the "multipart/form-data" as I wanted. So I need help
data = {
'name': 'erhan',
'last_name': 'koçlar',
'gender.code': 1,
'gender1.gender2.gender3.gender4.code': 1,
'tags[5]': 'TAG_2',
'tags[0]': 'TAG_1',
'tags[1]': 'TAG_2',
'persons[0].name': 'erhan',
'persons[1].name': 'ercan',
'files[0].file': 'file1',
'files[0].name': 'file_name_1',
'section_files[0]': 'file2'
'rows[0][0]': 'col_1',
'rows[0][1]': 'col_2',
'rows[1][0]': 'row_1',
'rows[1][1]': 'row_2'
}
#after convert
data = {
'name':'erhan',
'last_name': 'koçlar',
'gender': { 'code': 1 },
'gender1': { 'gender2': { 'gender3': { 'gender4': { 'code': 1 } } } },
'tags': [ 'TAG_1', 'TAG_2', 'TAG_2' ],
'persons': [ {'name':'erhan'}, {'name':'ercan'} ],
'files': [ {'file': 'file1', 'name':'file_name_1' }],
'section_files': [ 'file2' ],
'rows': [ [ 'col_1', 'col_2' ], [ 'row_1', 'row_2' ] ],
}
Something like this can do the Job. Note the modification I changed files[0].name to files[1].name as it would overwrite the field 0.
import re
data = {
'name': 'erhan',
'last_name': 'koçlar',
'gender.code': 1,
'gender1.gender2.gender3.gender4.code': 1,
'tags[5]': 'TAG_2',
'tags[0]': 'TAG_1',
'tags[1]': 'TAG_2',
'persons[0].name': 'erhan',
'persons[1].name': 'ercan',
'files[0].file': 'file1',
'files[1].name': 'file_name_1',
'section_files[0]': 'file2',
'rows[0][0]': 'col_1',
'rows[0][1]': 'col_2',
'rows[1][0]': 'row_1',
'rows[1][1]': 'row_2'
}
def buildSubDict(dkey,value):
keys = dkey.split('.')
while keys:
cdict = {keys.pop():value}
value = cdict
return cdict
def buildArray(keys, uniqueName, data):
aSize = None
for key in keys:
match =re.findall('[(\d+)]',key)
m = [[int(m)] for m in match]
if not aSize:
aSize = m
else:
aSize = [a + b for a,b in zip(aSize,m)]
aSize = [max(i)+1 for i in aSize]
res = ''
while aSize:
res = [res if isinstance(res, str) else res.copy() for n in range(aSize.pop())]
for key in keys:
match =re.findall('[(\d+)]',key)
ndim = [int(m) for m in match]
templst = []
citem = res
for idx in ndim:
new = citem[idx]
if not isinstance(new, list):
citem[idx] = data[key]
data.pop(key)
break
citem = new
data[uniqueName]=res
newKeys = {}
for key in dict(data):
subkey = None
if '.' in key:
sDict = buildSubDict(key,data[key])
data[list(sDict.keys())[0]] = sDict[list(sDict.keys())[0]]
data.pop(key)
uniqueKeys = set([i.split('[')[0] for i in data.keys() if '[' in i ])
for ukey in uniqueKeys:
subkeys = []
for key in data:
if ukey+'[' in key:
subkeys.append(key)
buildArray(subkeys, ukey, data)
After processing, data will look like this:
{'name': 'erhan',
'last_name': 'koçlar',
'gender': {'code': 1},
'gender1': {'gender2': {'gender3': {'gender4': {'code': 1}}}},
'section_files': ['file2'],
'persons': [{'name': 'erhan'}, {'name': 'ercan'}],
'rows': [['col_1', 'col_2'], ['row_1', 'row_2']],
'tags': ['TAG_1', 'TAG_2', '', '', '', 'TAG_2'],
'files': [{'file': 'file1'}, {'name': 'file_name_1'}]}
Also note the different implementation of tags, compared to your question. You can filter out the '' by setting them to None instead and then do a list comprehension [i for i in myList if i] to filter them out.

creating a list of dictionary in python

from the given input
lists = ["7ee57f24", "deadbeef"]
I want to get the following output
l1': [
{
'd':
{
'id': '7ee57f24'
}
},
{
'd':
{
'id': 'deadbeed'
}
}
]
I have tried this code
lists = ["7ee57f24", "deadbeef"]
l1 = {"d":[{"id": lis} for lis in lists]}
print(l1)
but it gives me wrong output
{'d': [{'id': '7ee57f24'}, {'id': 'deadbeef'}]}
Use the following:
lists = ["7ee57f24", "deadbeef"]
l1 = [
{"d": {"id": id_}}
for id_ in lists
]
print(l1)
Output:
[{'d': {'id': '7ee57f24'}}, {'d': {'id': 'deadbeef'}}]

How to use two different .format() with using one for-loop

So I have been trying to figure out how I can print out two different formats using one for loop. I would like to provide the code before explaining my issue
fullList = [
{
'url': 'www.randomsite.com/251293',
'numbers': '7.5'
},
{
'url': 'www.randomsite.com/251294',
'numbers': '8'
},
{
'url': 'www.randomsite.com/251295',
'numbers': '8.5'
},
{
'url': 'www.randomsite.com/251296',
'numbers': '9'
},
{
'url': 'www.randomsite.com/251297',
'numbers': '9.5'
}
]
#fullList = [
# {
# 'numbers': '7.5'
# },
# {
# 'numbers': '8'
# },
# {
# 'numbers': '8.5'
# },
# {
# 'numbers': '9'
# },
# {
# 'numbers': '9.5'
# }
#]
try:
numbersList = []
for numbers in fullList:
numbersList.append('{}{}'.format('{}'.format(numbers.get('url') if numbers.get('url') else ''), numbers.get('numbers')))
print(numbersList)
except Exception:
pass
and what I am looking for outcome is:
If url is in the list: print('<url|numbers>') meaning the format would be <url|numbers>
If no url is in the list: print(numbers) and the print here should only give the numbers - I sometimes just want the numbers, meaning that in the list I removed all URL's so it will only remain numbers.
My problem is that I dont know how I can combine these two into one format. So far I am able to print out only numbers with the code I have provided.
Use normal if/else. It will be more readable. And you have only one format.
for numbers in fullList:
if numbers.get('url'):
numbersList.append('{}|{}'.format(numbers.get('url'), numbers.get('numbers'))
else:
numbersList.append(numbers.get('numbers'))
You can solve this problem and it will look more pythonic this way:
fullList = [
{'url': 'www.randomsite.com/251293', 'numbers': '7.5'},
{'url': 'www.randomsite.com/251294', 'numbers': '8'},
{'url': 'www.randomsite.com/251295', 'numbers': '8.5'},
{'url': 'www.randomsite.com/251296', 'numbers': '9'},
{'url': 'www.randomsite.com/251297', 'numbers': '9.5'},
{'numbers': '100'}
]
[(x['url'] + '|' + x['numbers']) if x.get('url') else x['numbers'] for x in fullList ]
You are using list comprehensions, minimizing nesting etc.
One solution is to select all values in each subdict and join them with a custom delimiter. In this way, you don't care if the key/value exist or not.
# Let's consider partial data
fullList = [
{
'url': 'www.randomsite.com/251293',
'numbers': '7.5'
},
{
'url': 'www.randomsite.com/251294',
'numbers': '8'
},
{
'url': 'www.randomsite.com/251295',
'numbers': '8.5'
},
{
'url': 'www.randomsite.com/251296',
},
{
'numbers': '9.5'
}
]
numbersList = []
for element in fullList:
numbersList.append("|".join([element[v] for v in element.keys()]))
print(numbersList)
# ['www.randomsite.com/251293|7.5', 'www.randomsite.com/251294|8',
# 'www.randomsite.com/251295|8.5', 'www.randomsite.com/251296', '9.5']
You can do it in one line with list comprehension:
output = ["|".join([element[v] for v in element.keys()]) for element in fullList]
print(output)
# ['www.randomsite.com/251293|7.5', 'www.randomsite.com/251294|8',
# 'www.randomsite.com/251295|8.5', 'www.randomsite.com/251296', '9.5']
Using list comprehension
Ex.
fullList = [
{'url': 'www.randomsite.com/251293','numbers': '7.5'},
{'url': 'www.randomsite.com/251294','numbers': '8'},
{'url': 'www.randomsite.com/251295','numbers': '8.5'},
{'url': 'www.randomsite.com/251296','numbers': '9'},
{'url': 'www.randomsite.com/251297','numbers': '9.5'}
]
list1 = [ "{0}|{1}".format(x['url'],x['numbers']) for x in fullList ]
print(list1)
O/P:
['www.randomsite.com/251293|7.5', 'www.randomsite.com/251294|8', 'www.randomsite.com/251295|8.5', 'www.randomsite.com/251296|9', 'www.randomsite.com/251297|9.5']
OR
for the updated question, if the dictionary does not contain url
fullList = [
{'url': 'www.randomsite.com/251296','numbers': '9'},
{'numbers': '9.5'}
]
list1 = [ "{0}{1}".format((x.get('url')+'|' if 'url' in x else ''),x.get('numbers','')) for x in fullList ]
print(list1)
O/P:
['www.randomsite.com/251296|9', '9.5']

How can I create a nested dictionary object from tree-like file-directory text-file?

I have a tree-structure separated by tabs and lines like this:
a
\t1
\t2
\t3
\t\tb
\t\tc
\t4
\t5
And I am looking to turn this into:
{
'name': 'a',
'children': [
{'name': '1'},
{'name': '2'},
{
'name': '3'
'children': [
{'name': 'b'},
{'name': 'c'}
]
},
{'name': '4'},
{'name': '5'}
]
}
for a d3.js collapsible tree data input. I am assuming I have to use recursion in some way but I cannot figure out how.
I have tried turning the input into a list like this:
[('a',0), ('1',1), ('2',1), ('3',1), ('b',2), ('c',2), ('4',1), ('5',1)]
using this code:
def parser():
#run from root `retail-tree`: `python3 src/main.py`
l, all_line_details = list(), list()
with open('assets/retail') as f:
for line in f:
line = line.rstrip('\n ')
splitline = line.split(' ')
tup = (splitline[-1], len(splitline)-1)
l.append(splitline)
all_line_details.append(tup)
print(tup)
return all_line_details
Here, the first element is the string itself and the second is the number of tabs there are in that line. Unsure of the recursion step to accomplish this. Appreciate any help!
You can use a function that uses re.findall with a regex that matches a line as the name of the node, followed by 0 or more lines that start with a tab, grouped as the children, and then recursively builds the same structure for the children after stripping the first tab of each line from the children string:
import re
def parser(s):
output = []
for name, children in re.findall(r'(.*)\n((?:\t.*\n)*)', s):
node = {'name': name}
if children:
node.update({'children': parser(''.join(line[1:] for line in children.splitlines(True)))})
output.append(node)
return output
so that given:
s = '''a
\t1
\t2
\t3
\t\tb
\t\tc
\t4
\t5
'''
parser(s)[0] returns:
{'name': 'a',
'children': [{'name': '1'},
{'name': '2'},
{'name': '3', 'children': [{'name': 'b'}, {'name': 'c'}]},
{'name': '4'},
{'name': '5'}]}
Working from the list structure you provided from your own parser function:
def make_tree(lines, tab_count=0):
tree = []
index = 0
while index < len(lines):
if lines[index][1] == tab_count:
node = {"name": lines[index][0]}
children, lines_read = make_tree(lines[index + 1:], tab_count + 1)
if children:
node["children"] = children
index += lines_read
tree.append(node)
else:
break
index += 1
return tree, index
Test cases:
lines = [("a", 0), ("1", 1), ("2", 1), ("3", 1), ("b", 2), ("c", 2), ("4", 1), ("5", 1)]
test_1 = make_tree([("a", 0)])
assert test_1[0] == [{"name": "a"}], test_1
test_2 = make_tree([("a", 0), ("b", 1)])
assert test_2[0] == [{"name": "a", "children": [{"name": "b"}]}], test_2
test_3 = make_tree(lines)
expected_3 = [
{
"name": "a",
"children": [
{"name": "1"},
{"name": "2"},
{"name": "3", "children": [{"name": "b"}, {"name": "c"}]},
{"name": "4"},
{"name": "5"},
],
}
]
assert test_3[0] == expected_3, test_3
Note the output is wrapped in a list in case your source file has more than one root node (i.e. more than one line with no leading tabs), and also for neatness of the recursion.

Python way to join subsets of an element into a string?

I have this code...it works, but is there a better way to do it?
So if the participant list is [ { 'Id': 5, 'name':'bob'}, {'Id': 4, 'name': 'sally'} ], result should be '5, 4'.
participant_list = obj['participants']
id_num = []
for participant in participant_list:
id_num.append(str(participant['Id']))
result = ",".join(id_num)
Use a list comprehension with str.join:
>>> participant_list = [ { 'Id': 5, 'name':'bob'}, {'Id': 4, 'name': 'sally'} ]
>>> ", ".join([str(p["Id"]) for p in participant_list])
'5, 4'
>>>
Using map(), This works -
>>> participant = [ { 'Id': 5, 'name':'bob'}, {'Id': 4, 'name': 'sally'} ]
>>> ",".join(map(lambda x: str(x['Id']), participant))
'5,4'
How about:
>>> ','.join([str(i['Id']) for i in participant_list])
'5,4'

Categories