Modify list item based on key:value match python - python

Let's say we have the following list:
my_list = [{'id': '1', 'status':'new'},
{'id': '2', 'status':'closed'},
{'id': '3', 'status':'new'}]
Is there a way to change the 'status' of the dict with 'id':'1'?
This way the list will result in:
my_list = [{'id': '1', 'status':'changed'}, # <---
{'id': '2', 'status':'closed'},
{'id': '3', 'status':'new'}]
I see that one approach could be something like:
for i in range(len(my_list)):
if my_list[i]['id'] == 'desired_id':
my_list[i]['status'] = 'desired_status'
break
Is this the only option or is there a "nicer" approach?

Unless you have an option to change your data's structure (a dict of dicts for example instead of list of dicts), then no - since it's a list, you'll have to loop over it to find the dict with the matching id. So, if you're confined to using a list, the only thing to change is to loop on items instead of indices:
for d in my_list:
if d['id'] == 'desired_id':
d['status'] = 'desired_status'
break
else:
print("There is no ID matching the desired search")
But, if you can change your data to be, for example:
my_dict = {'1': {'status': 'new'},
'2': {'status': 'closed'},
'3': {'status': 'new'}}
...then there is no need for loops at all (that is assuming that ids can't repeat):
try:
my_dict['desired_id']['status'] = 'desired_status'
except KeyError:
print("There is no ID matching the desired search")

Since this a list of dict. looping through it would be the way that strikes my mind :
my_list = [{'id': '1', 'status':'new'},{'id': '2', 'status':'closed'},{'id': '3', 'status':'new'}]
for i,j in enumerate(my_list):
if j['id'] == '1':
my_list[i]['status']='changed'
print(my_list)

Related

Double Nesting defaultdict

Poked around but couldn't figure it out, probably a very simple solution but please help me understand.
Source (sample.txt):
1,1,2,3
2,3,2,4,4
This:
import csv
from collections import defaultdict
input = "sample.txt"
with open(input) as f:
r = csv.reader(f)
d = defaultdict(list)
rlabel = 1
for row in r:
d[rlabel].append(row)
rlabel += 1
print(d)
Gets This:
defaultdict(<class 'list'>, {1: [['1', '1', '2', '3']], 2: [['2', '3', '2', '4', '4']]})
Why are there double brackets around my lists?
Why are there double brackets around my lists?
Your code works exactly as expected. The key point is the usage of extend and append.
append adds the parameter you passed as a single element. Due to a list is an object and your defaultdict class is list, so the list is appended as a list of lists.
extend method iterate in the input and extend the original list by adding all elements from an iterable.
So, in this case, if you want to add a single list to your defaultdict you should use list.extend method. And your output will be:
defaultdict(<class 'list'>, {1: ['1', '1', '2', '3'], 2: ['2', '3', '2', '4', '4']})
With a defaultdict, when a key is created a default value is associated to it
>>> d = defaultdict(list)
>>> 'a' in d[1]
False
>>> d
defaultdict(<class 'list'>, {1: []})
Given that your row is a list, you are appending a list to the list associated with the key.
To add the elements you can do
d[rlabel]+= row

How can I remove a value in this list if it is not inside one of the keys of this nested dictionary?

Currently, I have the following weird nested dictionary:
d = {'start': {'0': {'start', 'near'}, '1': {'start'}}, 'near': {'1': {'end'}}, 'end': {}}
And the following list:
l = ['1', '0', '1', 'x', '0', '1']
If one of the values in l are not in one of the keys and values in the dictionary, remove it from the list.
So for example, in the list, there is an "x" and I am trying to see if there is anyway I can remove it with either l.remove() or if del / pop is better.
The thing that is giving me troubles is the dictionary.
What I have going so far:
d = {'start': {'0': {'start', 'near'}, '1': {'start'}}, 'near': {'1': {'end'}}, 'end': {}}
l = ['1', '0', '1', 'x', '0', '1']
for key, value in d.items():
for keys, values in value.items():
for number in l:
Get the set of keys:
keys = set.union(*[set(x.keys()) for x in d.values()])
#{'0', '1'}
Filter out the non-keys:
result = [item for item in l if item in keys]
#['1', '0', '1', '0', '1']

create a list of dictionaries from two lists of tuples

I have a set of tuples:
users = set(("test#a.com","password"),("test#b.com","password"))
but could be simplified to a set...and a list of tuples:
licences = [("test#a.com","22"),("test#a.com","23"),("test#b.com","12")]
For every entry of the list the username could be repeated with different "licence" values.
I need to build a list of dictionaries like this:
[{"user":"test#a.com", "licences":["22","23"]},{"user":"test#b.com", "licences":["12"]}]
What I've done so far is this:
licenzadiz = []
for num,user in enumerate(users):
licenzadiz.append({'user': user[0], 'licences': []})
for num2,licence in enumerate(licences):
if user[0] == licence[0]:
licenzadiz[num]['licences'].append(licence[1])
that is working well. BUT I wonder if there are more elegant solutions to my problem.
You can get fancy with nested default dicts:
from collections import defaultdict
items = [('A','1'),('A','3'),('A','2'),
('B','0'),('B','4'),('B','-1'),
('C','7'),('C','6'),('C','12')]
d = defaultdict(lambda: defaultdict(list))
for use,lic in items:
d[use]['username'] = use #<-- Overwrites each time an already known key is found, but thats ok
d[use]['licence'].append(lic)
#Just for printout
for use in d:
print d[use]
print d[use]['username']
print d[use]['licence']
Output:
defaultdict(<type 'list'>, {'username': 'A', 'licence': ['1', '3', '2']})
A
['1', '3', '2']
defaultdict(<type 'list'>, {'username': 'C', 'licence': ['7', '6', '12']})
C
['7', '6', '12']
defaultdict(<type 'list'>, {'username': 'B', 'licence': ['0', '4', '-1']})
B
['0', '4', '-1']
data = {}
for num2,(email, license) in enumerate(licenze):
data.setdefault(email,[]).append(license)
print data #dictionary of email:[licenses,..]
#or
print data.items() # if you want a list
I guess ... i think

why does this output returns different value every time? [duplicate]

This question already has answers here:
Why is the order in dictionaries and sets arbitrary?
(5 answers)
Closed 6 years ago.
def solution(dict, output, ini):
if (dict == None):
return None
else:
for key in dict:
if str(type(dict[key])) != str(type({})):
print(key)
output[ini + key] = dict[key]
else:
return solution(dict[key], output, ini + key + '.')
return output
a = {
'Key1': '1',
'Key2': {
'a': '2',
'b': '3',
'c': {
'd': '3',
'e': '1'
}
}
}
print(solution(a, {}, ""))
Hello I am trying to make a function that flattens nested dictionary.
For example a should print:
{'Key2.b': '3', 'Key1': '1', 'Key2.c.d': '3', 'Key2.a': '2', 'Key2.c.e': '1'}
But right now the code randomly gives me back the correct answer but from a range of 0-5 such as
{'Key2.b': '3', 'Key1': '1', 'Key2.c.d': '3', 'Key2.a': '2'},
{'Key2.b': '3', 'Key2.c.d': '3'}
I found out that if i get rid of the "return" in my else statement it would work but i am not sure why that is? Can anybody help me with it
When you iterate over the keys in dict with for key in dict: it will return the keys in random order since elements in dictionaries aren't ordered. If Key1 is processed first it will be added to output and on the next iteration Key2 will be recursively processed. Now if the order is different and Key2 is processed first then return solution(dict[key],output,ini+key+'.') will cause the result to be returned before Key1 gets added to output. Same goes with nested elements within Key2. If you remove the return statement then all the keys will be processed no matter the iteration order and you'll get the expected output.
as you put the result in output, you should not return at else,otherwise, the for loop will break, and the rest keys will be ignored.(just remove return, and it will do work)

How to combine dictionaries within an array based on the same key-value pairs?

For example in the below list, I'd like to combine all dictionaries that share the same 'id' and 'name.'
Input:
l = [{'id':'1','name':'a','key1':'1'},
{'id':'1','name':'a','key2':'3'},
{'id':'1','name':'a','key3':'4'},
{'id':'2','name':'a','key5':'1'},
{'id':'2','name':'a','key7':'c'},
{'id':'1','name':'b','key5':'1'}]
Desired Result:
l = [{'id':'1','name':'a','key1':'1','key2':'3','key3':'4'},
{'id':'2','name':'a','key5':'1','key7':'c'},
{'id':'1','name':'b','key5':'1'}]
If possible, I'd like the function to also take different number of arguments for which keys the dictionaries would have to share for them to combine. For example, if I just wanted to combine based on just the 'id' instead of the 'key' and the 'name,' the result would be different.
itertools.groupby does the grouping for you. All that's left to do then is to merge the list of iterables of dictionaries (grouped by id) into a list of dictionaries:
>>> import itertools
>>> l = [ dict(reduce(lambda a, b: a + b.items(), lst, [])) for lst in \
[ list(grouper) for key, grouper in \
itertools.groupby(l, lambda x: (x["id"], x["name"])) ] \
]
>>> l
[{'id': '1', 'key1': '1', 'key2': '3', 'key3': '4', 'name': 'a'},
{'id': '2', 'key5': '1', 'key7': 'c', 'name': 'a'},
{'id': '1', 'key5': '1', 'name': 'b'}]
That's clearly not the most readable version to do it; you should probably use a helper function that merges the dictionaries instead of the nested list comprehensions.
Traditional Way :D
result = []
for item in l :
check = False
# check item, is it exist in result yet (r_item)
for r_item in result :
if item['id'] == r_item['id'] and item['name'] == r_item['name'] :
# if found, add all key to r_item ( previous record)
check = True
r_item.update( item )
if check == False :
# if not found, add item to result (new record)
result.append( item )

Categories

Resources