Change list items that are within a dictionary - python

I have a dictionary like this in my django project which correspond to a config field:
{u'active': True,
u'alert': {u'item_sent': True,
u'emails': [u'test#test.com', u'test#test.com'],
u'job': u'500.00',
u'in_negative': False}}
How do I loop through each of the emails above and append '.fake' to the end of them? I was going for this - "test#test.com.fake"
I was doing this but it didn't change the list items:
for p in practice:
email_count = len(p.config['alert']['emails'])
if email_count > 0:
print 'WE HAVE EMAILS'
i = 0
while i < email_count:
p.config['alert']['emails'][i] += '.fake'
print p.config['alert']['emails'][i]
i += 1
p.save()

I cannot see why your code fails to change the list items, but try this:
for p in practice:
p.config['alert']['emails'] = [e + '.fake' for e in p.config['alert']['emails']]

Turns out I needed to assign p.config to a variable first before editing it. 'P' represents a practice object.
for p in practices:
config = p.config
config['alert']['emails'] = [
'.{0}.fake'.format(x)
for x in config['alert']['emails']
]
p.config = config
p.save()

Related

I want to create a list of lists

I know there is quite a number of similar questions on stackoverflow but they don't seem to be solving my problem. If you look at my code below, you can see that I am creating a temp list of ads called "tempAdList" and when the if condition evaluate true I am creating a list of lists called "ad_list". I am appending to "ad_list" so I am expecting that everytime the "if statement" evaluates true a new list of 4 ads is appended to "ad_list" but for whatever reason I am getting below output which is not what i am looking for. what am I doing wrong here?
ads = Advert.objects.all()
counter = 1
tempAdList = []
ad_list = []
for i, ad in enumerate(ads):
tempAdList.append(ad)
if counter == 4:
# print(tempAdList)
ad_list.append(tempAdList)
print(ad_list)
tempAdList.clear()
counter = 0
counter += 1
adsNum = len(ads)
# print("i = {} and adsNum = {}".format(i, adsNum))
if i == adsNum -1 and adsNum % 4 != 0:
ad_list.append(tempAdList)
output:
Using the clear-method on a list also affects all references to it, e.g.
>>a = [1, 2, 3]
>>b = a
>>a.clear()
>>print('a =',a)
a = []
>>print('b =',b)
b = []
So what you are doing in ad_list.append(tempAdList) is to repeatedly add references to the same object to ad_list, i.e. each time you update tempAdList, the same update is done for each of those references. What you really want to do is reset tempAdList with a new object, so replace tempAdList.clear() with tempAdList=[].
If you just want a list of lists, where inner lists are having 4 elements.
You can try something like :
new_list = [ads[i:i+4] for i in range(0, len(ads), 4)]
Every time you do tempAdlist.clear(), you cleared all elements of the list. But because you appended the list to ad_list, you basically cleared it there too. so you have one less list. This is because of the nature of lists being referenced instead of recreated. What you want is to create a list from tempAdlist when appending, like so: ad_list.append(list(tempAdlist)) this way it will be a whole new list from the tempAdlist. Essentially your code becomes:
ads = Advert.objects.all()
counter = 1
tempAdList = []
ad_list = []
for i, ad in enumerate(ads):
tempAdList.append(ad)
if counter == 4:
# print(tempAdList)
ad_list.append(list(tempAdList))
print(ad_list)
tempAdList.clear()
counter = 0
counter += 1
adsNum = len(ads)
# print("i = {} and adsNum = {}".format(i, adsNum))
if i == adsNum -1 and adsNum % 4 != 0:
ad_list.append(list(tempAdList))

Python Creating a dictionary of dictionaries structure, nested values are the same

I'm attempting to build a data structure that can change in size and be posted to Firebase. The issue I am seeing is during the construction of the data structure. I have the following code written:
for i in range(len(results)):
designData = {"Design Flag" : results[i][5],
"performance" : results[i][6]}
for j in range(len(objectiveNameArray)):
objectives[objectiveNameArray[j]] = results[i][columnHeaders.index(objectiveNameArray[j])]
designData["objectives"] = copy.copy(objectives)
for k in range(len(variableNameArray)):
variables[variableNameArray[k]] = results[i][columnHeaders.index(variableNameArray[k])]
designData["variables"] = copy.copy(variables)
for l in range(len(responseNameArray)):
responses[responseNameArray[l]] = results[i][columnHeaders.index(responseNameArray[l])]
designData["responses"] = copy.copy(responses)
for m in range(len(constraintNameArray)):
constraintViolated = False
if constraintNameArray[m][1] == "More than":
if results[i][columnHeaders.index(constraintNameArray[m][0])] > constraintNameArray[m][2]:
constraintViolated = True
else:
constraintViolated = False
elif constraintNameArray[m][1] == "Less than":
if results[i][columnHeaders.index(constraintNameArray[m][0])] < constraintNameArray[m][2]:
constraintViolated = True
else:
constraintViolated = False
if constraintNameArray[m][0] in constraints:
if constraints[constraintNameArray[m][0]]["violated"] == True:
constraintViolated = True
constraints[constraintNameArray[m][0]] = {"value" : results[i][columnHeaders.index(constraintNameArray[m][0])], "violated" : constraintViolated}
designData["constraints"] = copy.copy(constraints)
data[studyName][results[i][4]] = designData
When I include print(designData) inside of the for loop, I see that my results are changing as expected for each loop iteration.
However, if I include print(data) outside of the for loop, I get a data structure where the values added by the results array are all the same values for each iteration of the loop even though the key is different.
Comparing print(data) and print(designData)
I apologize in advance if this isn't enough information this is my first post on Stack so please be patient with me.
It is probably because you put the variables like objectives, variables, responses directly to the designData. Try the following:
import copy
....
designData['objectives'] = copy.copy(objectives)
....
designData['variables'] = copy.copy(variables)
....
designData['responses'] = copy.copy(responses)
For similar questions, see copy a list.

Split list based on first character - Python

I am new to Python and can't quite figure out a solution to my Problem. I would like to split a list into two lists, based on what the list item starts with. My list looks like this, each line represents an item (yes this is not the correct list notation, but for a better overview i'll leave it like this) :
***
**
.param
+foo = bar
+foofoo = barbar
+foofoofoo = barbarbar
.model
+spam = eggs
+spamspam = eggseggs
+spamspamspam = eggseggseggs
So I want a list that contains all lines starting with a '+' between .param and .model and another list that contains all lines starting with a '+' after model until the end.
I have looked at enumerate() and split(), but since I have a list and not a string and am not trying to match whole items in the list, I'm not sure how to implement them.
What I have is this:
paramList = []
for line in newContent:
while line.startswith('+'):
paramList.append(line)
if line.startswith('.'):
break
This is just my try to create the first list. The Problem is, the code reads the second block of '+'s as well because break just Exits the while Loop, not the for Loop.
I hope you can understand my question and thanks in advance for any pointers!
What you want is really a simple task that can be accomplish using list slices and list comprehension:
data = ['**','***','.param','+foo = bar','+foofoo = barbar','+foofoofoo = barbarbar',
'.model','+spam = eggs','+spamspam = eggseggs','+spamspamspam = eggseggseggs']
# First get the interesting positions.
param_tag_pos = data.index('.param')
model_tag_pos = data.index('.model')
# Get all elements between tags.
params = [param for param in data[param_tag_pos + 1: model_tag_pos] if param.startswith('+')]
models = [model for model in data[model_tag_pos + 1: -1] if model.startswith('+')]
print(params)
print(models)
Output
>>> ['+foo = bar', '+foofoo = barbar', '+foofoofoo = barbarbar']
>>> ['+spam = eggs', '+spamspam = eggseggs']
Answer to comment:
Suppose you have a list containing numbers from 0 up to 5.
l = [0, 1, 2, 3, 4, 5]
Then using list slices you can select a subset of l:
another = l[2:5] # another is [2, 3, 4]
That what we are doing here:
data[param_tag_pos + 1: model_tag_pos]
And for your last question: ...how does python know param are the lines in data it should iterate over and what exactly does the first paramin param for paramdo?
Python doesn't know, You have to tell him.
First param is a variable name I'm using here, it cuold be x, list_items, whatever you want.
and I will translate the line of code to plain english for you:
# Pythonian
params = [param for param in data[param_tag_pos + 1: model_tag_pos] if param.startswith('+')]
# English
params is a list of "things", for each "thing" we can see in the list `data`
from position `param_tag_pos + 1` to position `model_tag_pos`, just if that "thing" starts with the character '+'.
data = {}
for line in newContent:
if line.startswith('.'):
cur_dict = {}
data[line[1:]] = cur_dict
elif line.startswith('+'):
key, value = line[1:].split(' = ', 1)
cur_dict[key] = value
This creates a dict of dicts:
{'model': {'spam': 'eggs',
'spamspam': 'eggseggs',
'spamspamspam': 'eggseggseggs'},
'param': {'foo': 'bar',
'foofoo': 'barbar',
'foofoofoo': 'barbarbar'}}
I am new to Python
Whoops. Don't bother with my answer then.
I want a list that contains all lines starting with a '+' between
.param and .model and another list that contains all lines starting
with a '+' after model until the end.
import itertools as it
import pprint
data = [
'***',
'**',
'.param',
'+foo = bar',
'+foofoo = barbar',
'+foofoofoo = barbarbar',
'.model',
'+spam = eggs',
'+spamspam = eggseggs',
'+spamspamspam = eggseggseggs',
]
results = [
list(group) for key, group in it.groupby(data, lambda s: s.startswith('+'))
if key
]
pprint.pprint(results)
print '-' * 20
print results[0]
print '-' * 20
pprint.pprint(results[1])
--output:--
[['+foo = bar', '+foofoo = barbar', '+foofoofoo = barbarbar'],
['+spam = eggs', '+spamspam = eggseggs', '+spamspamspam = eggseggseggs']]
--------------------
['+foo = bar', '+foofoo = barbar', '+foofoofoo = barbarbar']
--------------------
['+spam = eggs', '+spamspam = eggseggs', '+spamspamspam = eggseggseggs']
This thing here:
it.groupby(data, lambda x: x.startswith('+')
...tells python to create groups from the strings according to their first character. If the first character is a '+', then the string gets put into a True group. If the first character is not a '+', then the string gets put into a False group. However, there are more than two groups because consecutive False strings will form a group, and consecutive True strings will form a group.
Based on your data, the first three strings:
***
**
.param
will create one False group. Then, the next strings:
+foo = bar
+foofoo = barbar
+foofoofoo = barbarbar
will create one True group. Then the next string:
'.model'
will create another False group. Then the next strings:
'+spam = eggs'
'+spamspam = eggseggs'
'+spamspamspam = eggseggseggs'
will create another True group. The result will be something like:
{
False: [strs here],
True: [strs here],
False: [strs here],
True: [strs here]
}
Then it's just a matter of picking out each True group: if key, and then converting the corresponding group to a list: list(group).
Response to comment:
where exactly does python go through data, like how does it know s is
the data it's iterating over?
groupby() works like do_stuff() below:
def do_stuff(items, func):
for item in items:
print func(item)
#Create the arguments for do_stuff():
data = [1, 2, 3]
def my_func(x):
return x + 100
#Call do_stuff() with the proper argument types:
do_stuff(data, my_func) #Just like when calling groupby(), you provide some data
#and a function that you want applied to each item in data
--output:--
101
102
103
Which can also be written like this:
do_stuff(data, lambda x: x + 100)
lambda creates an anonymous function, which is convenient for simple functions which you don't need to refer to by name.
This list comprehension:
[
list(group)
for key, group in it.groupby(data, lambda s: s.startswith('+'))
if key
]
is equivalent to this:
results = []
for key, group in it.groupby(data, lambda s: s.startswith('+') ):
if key:
results.append(list(group))
It's clearer to explicitly write a for loop, however list comprehensions execute much faster. Here is some detail:
[
list(group) #The item you want to be in the results list for the current iteration of the loop here:
for key, group in it.groupby(data, lambda s: s.startswith('+')) #A for loop
if key #Only include the item for the current loop iteration in the results list if key is True
]
I would suggest doing things step by step.
1) Grab every word from the array separately.
2) Grab the first letter of the word.
3) Look if that is a '+' or '.'
Example code:
import re
class Dark():
def __init__(self):
# Array
x = ['+Hello', '.World', '+Hobbits', '+Dwarves', '.Orcs']
xPlus = []
xDot = []
# Values
i = 0
# Look through every word in the array one by one.
while (i != len(x)):
# Grab every word (s), and convert to string (y).
s = x[i:i+1]
y = '\n'.join(s)
# Print word
print(y)
# Grab the first letter.
letter = y[:1]
if (letter == '+'):
xPlus.append(y)
elif (letter == '.'):
xDot.append(y)
else:
pass
# Add +1
i = i + 1
# Print lists
print(xPlus)
print(xDot)
#Run class
Dark()

in-place modification of strings within a list?

I'm trying to change some elements of a list based on the properties of previous ones. Because I need to assign an intermediate variable, I don't think this can be done as a list comprehension. The following code, with comment, is what I'm trying to achieve:
for H in header:
if "lower" in H.lower():
pref="lower"
elif "higher" in H.lower():
pref="higher"
if header.count(H) > 1:
# change H inplace
H = pref+H
The best solution I've come up with is:
for ii,H in enumerate(header):
if "lower" in H.lower():
pref="lower"
elif "higher" in H.lower():
pref="higher"
if header.count(H) > 1:
header[ii] = pref+H
It doesn't quite work, and feels un-pythonic to me because of the indexing. Is there a better way to do this?
Concrete example:
header = ['LowerLevel','Term','J','UpperLevel','Term','J']
desired output:
header = ['LowerLevel','LowerTerm','LowerJ','UpperLevel','UpperTerm','UpperJ']
Note that neither of my solutions work: the former never modifies header at all, the latter only returns
header = ['LowerLevel','LowerTerm','LowerJ','UpperLevel','Term','J']
because count is wrong after the modifications.
header = ['LowerLevel','Term','J','UpperLevel','Term','J']
prefixes = ['lower', 'upper']
def prefixed(header):
prefix = ''
for h in header:
for p in prefixes:
if h.lower().startswith(p):
prefix, h = h[:len(p)], h[len(p):]
yield prefix + h
print list(prefixed(header))
I don't really know that this is better than what you had. It's different...
$ ./lower.py
['LowerLevel', 'LowerTerm', 'LowerJ', 'UpperLevel', 'UpperTerm', 'UpperJ']
something like this, using generator function:
In [62]: def func(lis):
pref=""
for x in lis:
if "lower" in x.lower():
pref="Lower"
elif "upper" in x.lower():
pref="Upper"
if header.count(x)>1:
yield pref+x
else:
yield x
....:
In [63]: list(func(header))
Out[63]: ['LowerLevel', 'LowerTerm', 'LowerJ', 'UpperLevel', 'UpperTerm', 'UpperJ']
This should work for the data you presented.
from collections import defaultdict
def find_dups(seq):
'''Finds duplicates in a sequence and returns a dict
of value:occurences'''
seen = defaultdict(int)
for curr in seq:
seen[curr] += 1
d = dict([(i, seen[i]) for i in seen if seen[i] > 1])
return d
if __name__ == '__main__':
header = ['LowerLevel','Term','J','UpperLevel','Term','J']
d = find_dups(header)
for i, s in enumerate(header):
if s in d:
if d[s] % 2:
pref = 'Upper'
else:
pref = 'Lower'
header[i] = pref + s
d[s] -= 1
But it give me the creeps to suggest anything, not knowing but a little about the entire set of data you will be working with.
good luck,
Mike

create dictionary with multiple keys and values

I would like to create a dictionary with multiple keys and values. At this point I am not sure that I am putting my question correctly. But here is an example of what I want to create :
patDct = {
'mkey1':{'key1':'val_a1', 'key2':'val_a2', 'key3':'val_a3'},
'mkey2':{'key1':'val_b1', 'key2':'val_b2', 'key3':'val_b3'},
....
}
I have two dictionaries and I am pulling information for 'mkey*', and 'val*' from them. 'key*' are strings.
I have a piece of code to create the dictionary without the 'mkey*', but that only prints out the last set of values. Following is what I have now.
"storedct" and "datadct" are two given dictionaries.
Here I would like 'mkey*' to get the value of "item".
patDct = dict()
for item in storedct :
for pattern in datadct :
if pattern in item :
patDct['key1'] = datadct[pattern]["dpath"]
patDct['key2'] = datadct[pattern]["mask"]
patDct['key3'] = storedct[item]
Thanks for any suggestion.
patDct = dict()
n=1
for item in storedct :
patDct["mkey%s"%n] = {}
p = patDct["mkey%s"%n]
for pattern in datadct :
if pattern in item :
p['key1'] = datadct[pattern]["dpath"]
p['key2'] = datadct[pattern]["mask"]
p['key3'] = storedct[item]
n +=1
print patDct
From what I understand from your code, I guess that:
patDct = dict()
i = 0
for item in storedct :
for pattern in datadct :
if pattern in item :
i = i + 1
new_item = {}
new_item['key1'] = datadct[pattern]["dpath"]
new_item['key2'] = datadct[pattern]["mask"]
new_item['key3'] = storedct[item]
# I used a counter to generate the `mkey` values,
# not sure you want it that way
patDct['mkey{0}'.format(i)] = new_item
should not be far from your needs...

Categories

Resources