This question already has answers here:
Filling a python dictionary in for loop returns same values
(2 answers)
Closed 5 years ago.
Issue
I've made a for loop reading the contents of a list however when assigning two values to a dictionary and then appending that output to a list, the next value overwrites everything in the list
Desired outcome
I want to append multiple dictionaries to a list so when I run a for loop and print everything related to 'ip' it will print all the values associated with the dictionary value 'ip'.
Code
device = { 'ip': '', 'mac': '', 'username': 'admin', 'password': [], 'device type': '', }
listofdevices = []
def begin():
file = open("outputfromterminal")
contents = file.read()
contents = contents.split(',')[1:]
for x in contents:
# do some text stripping
x = x.split(' ')
device['ip']=x[0]
device['mac']=x[1]
listofdevices.append(device)
Sample code
the 1st index of contents is:
x[0] = '10.10.10.1'
x[1] = 'aa:bb:cc:dd'
The 2nd index of contents is:
x[0] = '20.20.20.1'
x[1] = 'qq:ww:ee:ee:rr'
What actually happens
listofdevices[0] 'ip': 20.20.20.1, 'mac': 'qq:ww:ee:ee:rr'
listofdevices[1] 'ip': 20.20.20.1, 'mac': 'qq:ww:ee:ee:rr'
Try this code. Each device was trying to edit the same copy of a dictionary.
listofdevices = []
def begin():
with open("outputfromterminal", 'r') as f:
contents = f.read().split(',')[1:]
for line in contents:
# do some text stripping
line = line.split(' ')
device = { 'ip': line[0],
'mac': line[1],
'username': 'admin',
'password': [],
'device type': '',
}
listofdevices.append(device)
You are not creating a new dictionary object each time. You are simply mutating the same object within each iteration. Try deep copying the dictionary using the copy module. Then after obtaining this copy, mutate it and append to list:
import copy
device = { 'ip': '', 'mac': '', 'username': 'admin', 'password': [], 'device type': '', }
listofdevices = []
def begin():
file = open("outputfromterminal")
contents = file.read()
contents = contents.split(',')[1:]
for x in contents:
device = copy.deepcopy(device) #creates a deep copy of the values of previous dictionary.
#device now references a completely new object
# do some text stripping
x = x.split(' ')
device['ip']=x[0]
device['mac']=x[1]
listofdevices.append(device)
The issue is due to the appending of the list. When you append an item(in your case a dictionary). It does not create a dictionary, But it simply places the reference.
It should work if you can initialise the dictionary in the for loop every time, So a new reference is created.
listofdevices = []
def begin():
file = open("outputfromterminal")
contents = file.read()
contents = contents.split(',')[1:]
for x in contents:
# do some text stripping
x = x.split(' ')
device = { 'ip': '', 'mac': '', 'username': 'admin', 'password': [], 'device type': '', }
device['ip']=x[0]
device['mac']=x[1]
listofdevices.append(device)
Related
How can i get needed value, because i send post request to other site and cant edit answer from site.
I have this dict from responded content:
{'username': 'DeadFinder', 'subscriptions': [{'subscription': 'default', 'expiry': '1635683460'}], 'ip': 'not at this life'}
How you can see in this dict there is a key subscriptions, i'm need value expiry(this is timestamp) but how can i get this value if when i'm trying to call this value i'm not see any results (code not gives needed value), maybe any variants how to get this value? I'm not finded anything like this.
Maybe my small part of code can smally help you but i doubt.
data1 = {f"hwid":"", "type":"login", "username": {username}, "pass": {password},
"sessionid":f"{response_cut2}", "name":"test_app", "ownerid":"5OLbm5S3fS"}
url1 = "nope"
response1 = requests.post(url1, data1)
data = response1.json()
#get = data.get('expiry')
file_write = open("test.txt", "w")
file_write.write(str(data))
file_write.close()
for key in data.keys():
if key == 'info':
print (data[key])
Are you trying to achieve this as result ?
data = {'username': 'DeadFinder', 'subscriptions': [{'subscription': 'default', 'expiry': '1635683460'}], 'ip': 'not at this life'}
print(data['subscriptions'][0]['expiry'])
# first get 'subscriptions' which returns an array,
# so use [0] to get this dict {'subscription': 'default', 'expiry': '1635683460'}
# then get 'expiry'
EDIT : In case subscriptions has multiple values then use for loop
subscriptions = data['subscriptions']
for subscription in subscriptions:
print(subscription['expiry'])
Output
1635683460
I am trying to run this code where data of a dictionary is saved in a separate csv file.
Here is the dict:
body = {
'dont-ask-for-email': 0,
'action': 'submit_user_review',
'post_id': 76196,
'email': email_random(),
'subscribe': 1,
'previous_hosting_id': prev_hosting_comp_random(),
'fb_token': '',
'title': review_title_random(),
'summary': summary_random(),
'score_pricing': star_random(),
'score_userfriendly': star_random(),
'score_support': star_random(),
'score_features': star_random(),
'hosting_type': hosting_type_random(),
'author': name_random(),
'social_link': '',
'site': '',
'screenshot[image][]': '',
'screenshot[description][]': '',
'user_data_process_agreement': 1,
'user_email_popup': '',
'subscribe_popup': 1,
'email_asked': 1
}
Now this is the code to write in a CSV file and finally save it:
columns = []
rows = []
chunks = body.split('}')
for chunk in chunks:
row = []
if len(chunk)>1:
entry = chunk.replace('{','').strip().split(',')
for e in entry:
item = e.strip().split(':')
if len(item)==2:
row.append(item[1])
if chunks.index(chunk)==0:
columns.append(item[0])
rows.append(row)
df = pd.DataFrame(rows, columns = columns)
df.head()
df.to_csv ('r3edata.csv', index = False, header = True)
but this is the error I get:
Traceback (most recent call last):
File "codeOffshoreupdated.py", line 125, in <module>
chunks = body.split('}')
AttributeError: 'dict' object has no attribute 'split'
I know that dict has no attribute named split but how do I fix it?
Edit:
format of the CSV I want:
dont-ask-for-email, action, post_id, email, subscribe, previous_hosting_id, fb_token, title, summary, score_pricing, score_userfriendly, score_support, score_features, hosting_type,author, social_link, site, screenshot[image][],screenshot[description][],user_data_process_agreement,user_email_popup,subscribe_popup,email_asked
0,'submit_user_review',76196,email_random(),1,prev_hosting_comp_random(),,review_title_random(),summary_random(),star_random(),star_random(),star_random(),star_random(),hosting_type_random(),name_random(),,,,,1,,1,1
Note: all these functions mentioned are return values
Edit2:
I am picking emails from the email_random() function like this:
def email_random():
with open('emaillist.txt') as emails:
read_emails = csv.reader(emails, delimiter = '\n')
return random.choice(list(read_emails))[0]
and the emaillist.txt is like this:
xyz#gmail.com
xya#gmail.com
xyb#gmail.com
xyc#gmail.com
xyd#gmail.com
other functions are also picking the data from the files like this too.
Since body is a dictionary, you don't have to a any manual parsing to get it into a CSV format.
If you want the function calls (like email_random()) to be written into the CSV as such, you need to wrap them into quotes (as I have done below). If you want them to resolve as function calls and write the results, you can keep them as they are.
import csv
def email_random():
return "john#example.com"
body = {
'dont-ask-for-email': 0,
'action': 'submit_user_review',
'post_id': 76196,
'email': email_random(),
'subscribe': 1,
'previous_hosting_id': "prev_hosting_comp_random()",
'fb_token': '',
'title': "review_title_random()",
'summary': "summary_random()",
'score_pricing': "star_random()",
'score_userfriendly': "star_random()",
'score_support': "star_random()",
'score_features': "star_random()",
'hosting_type': "hosting_type_random()",
'author': "name_random()",
'social_link': '',
'site': '',
'screenshot[image][]': '',
'screenshot[description][]': '',
'user_data_process_agreement': 1,
'user_email_popup': '',
'subscribe_popup': 1,
'email_asked': 1
}
with open('example.csv', 'w') as fhandle:
writer = csv.writer(fhandle)
items = body.items()
writer.writerow([key for key, value in items])
writer.writerow([value for key, value in items])
What we do here is:
with open('example.csv', 'w') as fhandle:
this opens a new file (named example.csv) with writing permissions ('w') and stores the reference into variable fhandle. If using with is not familiar to you, you can learn more about them from this PEP.
body.items() will return an iterable of tuples (this is done to guarantee dictionary items are returned in the same order). The output of this will look like [('dont-ask-for-email', 0), ('action', 'submit_user_review'), ...].
We can then write first all the keys using a list comprehension and to the next row, we write all the values.
This results in
dont-ask-for-email,action,post_id,email,subscribe,previous_hosting_id,fb_token,title,summary,score_pricing,score_userfriendly,score_support,score_features,hosting_type,author,social_link,site,screenshot[image][],screenshot[description][],user_data_process_agreement,user_email_popup,subscribe_popup,email_asked
0,submit_user_review,76196,john#example.com,1,prev_hosting_comp_random(),,review_title_random(),summary_random(),star_random(),star_random(),star_random(),star_random(),hosting_type_random(),name_random(),,,,,1,,1,1
I am a fairly new dev and trying to parse "id" values from this file. Running into the issue below.
My python code:
import ast
from pathlib import Path
file = Path.home() /'AppData'/'Roaming'/'user-preferences-prod'
with open(file, 'r') as f:
contents = f.read()
ids = ast.literal_eval(contents)
profileids = []
for data in ids:
test= data.get('id')
profileids.append(test)
print(profileids))
This returns the error: ValueError: malformed node or string: <_ast.Name object at 0x0000023D8DA4D2E8> at ids = ast.literal_eval(contents)
A snippet of the content in my file of interest:
{"settings":{"defaults":{"value1":,"value2":,"value3":null,"value4":null,"proxyid":null,"sites":{},"sizes":[],"value5":false},"value6":true,"value11":,"user":{"value9":"","value8": ,"value7":"","value10":""},"webhook":"},'profiles':[{'billing': {'address1': '', 'address2': '', 'city': '', 'country': 'United States', 'firstName': '', 'lastName': '', 'phone': '', 'postalCode': '', 'province': '', 'usesBillingInformation': False}, 'createdAt': 123231231213212, 'id': '23123123123213, 'name': ''
I need this code to be looped as there are multiple id values that I am interested in and need them all to be entered into a list.Hopefully I explained it all. the file type is "file" according to windows, I just view its contents with notepad.
It appears to me that you have a file with a string representation of a dict (dictionary). So, what you need to do is:
string_of_dict →ast.literal_eval()→ dict
Open file and read in the text into a string variable. Currently I think this string is going into ids.
Then convert the string representation of dict into a dict using ast library as shown below. Reference
import ast
string_of_dict = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
ast.literal_eval(string_of_dict)
Output:
{'muffin': 'lolz', 'foo': 'kitty'}
Solution
Something like this should most likely work. You may have to tweak it a little bit.
import ast
with open(file, 'r') as f:
contents = f.read()
ids = ast.literal_eval(contents)
profileids = []
for data in ids:
test= data.get('id')
profileids.append(test)
print(profileids)
This question already has answers here:
List on python appending always the same value [duplicate]
(5 answers)
Creating a list of dictionaries results in a list of copies of the same dictionary
(4 answers)
Closed 4 years ago.
I am trying to append dictionaries to a list here, problem is after appending both "Chrome" and "Firefox" values to the "list" i see only firefox.exe in list for both entries ..
any help is really appreciated.
See the print statement of dictionary where both values are different.
MyItems = ["ChromeSetup.exe","firefox.exe"]
listofitems = [{"appId": "ChromeID", 'id': "0","name": 'ChromeSetup.exe','_id': 'ChromeUnique'},{"appId": "FireFoxID", 'id': "0","name": 'firefox.exe','_id': 'FireFoxUnique'} ]
__id = ""
appId = ""
result = []
Dict = {"installerParameters":"","managedApp":{"_id":__id, "appId":appId},"postInstallAction":0,"postInstallScript":{"_id":"0"},"preInstallScript":{"_id":"0"}}
for app in MyItems:
for items in listofitems:
if items['name'] == app:
Dict["managedApp"]["_id"] = items['_id']
Dict["managedApp"]["appId"] = items['appId']
print("Dictionery",Dict)
result.append(Dict)
break
print("See the List", result)
Result:
Dictionery {'installerParameters': '', 'managedApp': {'_id': 'ChromeUnique', 'appId': 'ChromeID'}, 'postInstallAction': 0, 'postInstallScript': {'_id': '0'}, 'preInstallScript': {'_id': '0'}}
Dictionery {'installerParameters': '', 'managedApp': {'_id': 'FireFoxUnique', 'appId': 'FireFoxID'}, 'postInstallAction': 0, 'postInstallScript': {'_id': '0'}, 'preInstallScript': {'_id': '0'}}
See the List [{'installerParameters': '', 'managedApp': {'_id': 'FireFoxUnique', 'appId': 'FireFoxID'}, 'postInstallAction': 0, 'postInstallScript': {'_id': '0'}, 'preInstallScript': {'_id': '0'}}, {'installerParameters': '', 'managedApp': {'_id': 'FireFoxUnique', 'appId': 'FireFoxID'}, 'postInstallAction': 0, 'postInstallScript': {'_id': '0'}, 'preInstallScript': {'_id': '0'}}]
Define the dictionary in the for loop. You are currently writing to same dictionary object, and list holds reference to this object which itself is a reference. As a result you keep modifying the same object.
MyItems = ["ChromeSetup.exe","firefox.exe"]
listofitems = [{"appId": "ChromeID", 'id': "0","name": 'ChromeSetup.exe','_id': 'ChromeUnique'},{"appId": "FireFoxID", 'id': "0","name": 'firefox.exe','_id': 'FireFoxUnique'} ]
__id = ""
appId = ""
result = []
for app in MyItems:
for items in listofitems:
if items['name'] == app:
# I would try to find a better var name.
Dict = {"installerParameters":"","managedApp":{"_id":__id, "appId":appId},"postInstallAction":0,"postInstallScript":{"_id":"0"},"preInstallScript":{"_id":"0"}}
Dict["managedApp"]["_id"] = items['_id']
Dict["managedApp"]["appId"] = items['appId']
print("Dictionery",Dict)
result.append(Dict)
break
print("See the List", result)
Your dictionary object Dict is being overwritten during the second run of the loop. This is happening because you have defined the Dict above the loop. Better to define the Dict inside the loop.
I'm wondering if anyone has a sort of hacky / cool solution to this problem . I have a text file like so:
NAME:name
ID:id
PERSON:person
LOCATION:location
NAME:name
morenamestuff
ID:id
PERSON:person
LOCATION:location
JUNK
So I have some blocks that all contain lines that can be split into a dict, and some that cannot. How can I take lines without the : character and join them to the previous line? Here's what I'm currently doing
# loop through chunk
# the first element of dat is a Title, so skip that
key_map = dict(x.split(':') for x in dat[1:])
But I of course get an error because the second chunk has a line without the : character. So I wanted my dict to look something like this after correctly splitting it:
# there will be a key_map for each chunk of data
key_map['NAME'] == 'name morenamestuff' # 3rd line appended to previous
key_map['ID'] == 'id'
key_map['PERSON'] = 'person'
key_map['LOCATION'] = 'location
Solution
EDIT: Here's my final solution on github, and the full code here:
parseScript.py
import re
import string
bad_chars = '(){}"<>[] ' # characers we want to strip from the string
key_map = []
# parse file
with open("dat.txt") as f:
data = f.read()
data = data.strip('\n')
data = re.split('}|\[{', data)
# format file
with open("format.dat") as f:
formatData = [x.strip('\n') for x in f.readlines()]
data = filter(len, data)
# strip and split each station
for dat in data[1:-1]:
# perform black magic, don't even try to understand this
dat = dat.translate(string.maketrans("", "", ), bad_chars).split(',')
key_map.append(dict(x.split(':') for x in dat if ':' in x ))
if ':' not in dat[1]:key_map['NAME']+=dat[k][2]
for station in range(0, len(key_map)):
for opt in formatData:
print opt,":",key_map[station][opt]
print ""
dat.txt
View raw here
format.dat
NAME
STID
LONGITUDE
LATITUDE
ELEVATION
STATE
ID
out.dat
View raw here
When in doubt, write your own generator.
Add in itertools.groupby to chunk by groups of text delimited by whitespace breaks.
def chunker(s):
it = iter(s)
out = [next(it)]
for line in it:
if ':' in line or not line:
yield ' '.join(out)
out = []
out.append(line)
if out:
yield ' '.join(out)
usage:
from itertools import groupby
[dict(x.split(':') for x in g) for k,g in groupby(chunker(lines), bool) if k]
Out[65]:
[{'ID': 'id', 'LOCATION': 'location', 'NAME': 'name', 'PERSON': 'person'},
{'ID': 'id',
'LOCATION': 'location',
'NAME': 'name morenamestuff',
'PERSON': 'person'}]
(if those fields are always the same, I'd go with something like creating some namedtuples instead of a bunch of dicts)
from collections import namedtuple
Thing = namedtuple('Thing', 'ID LOCATION NAME PERSON')
[Thing(**dict(x.split(':') for x in g)) for k,g in groupby(chunker(lines), bool) if k]
Out[76]:
[Thing(ID='id', LOCATION='location', NAME='name', PERSON='person'),
Thing(ID='id', LOCATION='location', NAME='name morenamestuff', PERSON='person')]
Here is something that addresses all your requirements. It handles joining of multiple lines, ignoring blank lines, and ignoring junk lines that do not appear within a block. It is implemented as a generator that yields each dictionary as it is completed.
def parser(data):
d = {}
for line in data:
line = line.strip()
if not line:
if d:
yield d
d = {}
else:
if ':' in line:
key, value = line.split(':')
d[key] = value
else:
if d:
d[key] = '{} {}'.format(d[key], line)
if d:
yield d
When run with this data:
ignore me
NAME:name1
ID:id1
PERSON:person1
LOCATION:location1
NAME:name2
morenamestuff
ID:id2
PERSON:person2
LOCATION:location2
junk
and
other
stuff
NAME:name3
morenamestuff
and more
ID:id3
PERSON:person3
more person stuff
LOCATION:location3
JUNK
MORE JUNK
>>> for d in parser(open('data')):
... print d
{'PERSON': 'person1', 'LOCATION': 'location1', 'NAME': 'name1', 'ID': 'id1'}
{'PERSON': 'person2', 'LOCATION': 'location2', 'NAME': 'name2 morenamestuff', 'ID': 'id2'}
{'PERSON': 'person3 more person stuff', 'LOCATION': 'location3', 'NAME': 'name3 morenamestuff and more', 'ID': 'id3'}
You can grab the lot as a list:
>>> results = list(parser(open('data')))
>>> results
[{'PERSON': 'person1', 'LOCATION': 'location1', 'NAME': 'name1', 'ID': 'id1'}, {'PERSON': 'person2', 'LOCATION': 'location2', 'NAME': 'name2 morenamestuff', 'ID': 'id2'}, {'PERSON': 'person3 more person stuff', 'LOCATION': 'location3', 'NAME': 'name3 morenamestuff and more', 'ID': 'id3'}]
I don't find itertools or regex particularly nice to work with, here's a pure-python solution
separator = ':'
output = []
chunk = None
with open('/tmp/stuff.txt') as f:
for line in (x.strip() for x in f):
if not line:
# we are between 'chunks'
chunk, key = None, None
continue
if chunk is None:
# we are at the beginning of a new 'chunk'
chunk, key = {}, None
output.append(chunk)
if separator in line:
key, val = line.split(separator)
chunk[key] = val
else:
chunk[key] += line
not as elegant, as you requested, but this works
dat=[['NAME:name',
'ID:id',
'PERSON:person',
'LOCATION:location'],
['NAME:name',
'morenamestuff',
'ID:id',
'PERSON:person',
'LOCATION:location']]
k=1
key_map = dict(x.split(':') for x in dat[k] if ':' in x )
if ':' not in dat[k][1]:key_map['NAME']+=dat[k][1]
key_map>>
{'ID': 'id',
'LOCATION': 'location',
'NAME': 'namemorenamestuff',
'PERSON': 'person'}
Just add something to lines with no ":".
if line.find(':') == -1:
line=line+':None'
Then you won't get an error.