I'm struggling to get to a nested dictionary in a list.
L = [{'color':'yellow','fruit':'banana'},{'firstname':'Jack','lastname':'Black'}]
I can get the individual dictionaries with the following code.
for a in L:
print (a)
{'color': 'yellow', 'fruit': 'banana'}
{'lastname': 'Black', 'firstname': 'Jack'}
But now I only want the dictionary with lastname/firstname.
After the tips posted to my question, I came up with the following code.
def getPhotosFromAlbum(albumName):
availablePhotos = []
availableAlbums = getAlbumList()
print("Looking in album %s" %albumName)
for album in availableAlbums:
if albumName == album['AlbumName']:
#print ("found photos for album: ", albumName['KeyList'])
for photoRefKey in album['KeyList']:
print(getPhotoPath(photoRefKey))
availablePhotos.append(getPhotoPath(photoRefKey))
return availablePhotos
The idea behind this function is that I can parse the list in a HTML template file to display the photos. So my follow up question is: is this code ok or is there a more Python like approach to achieve the same result.
Darrell.
Use a list comprehension:
only_last_and_first = [d for d in L if 'firstname' in d and 'lastname' in d]
Now you have a list of dictionaries that have those two keys.
Demo:
>>> L = [{'color':'yellow','fruit':'banana'},{'firstname':'Jack','lastname':'Black'}]
>>> [d for d in L if 'firstname' in d and 'lastname' in d]
[{'lastname': 'Black', 'firstname': 'Jack'}]
If you only wanted the first such a dictionary, use next() and a generator expression:
first = next((d for d in L if 'firstname' in d and 'lastname' in d), None)
Here first will be set to either the first dictionary with the two keys, or None if there is no such dictionary.
If such syntax is a little daunting, here is a version with a for loop instead:
first = None
for d in L:
if 'firstname' in d and 'lastname' in d:
first = d
break
I build on Martijn's answer to propose a solution:
returning a print of items, just like in your example (and not a list)
checking that keys are exactly 'lastname' and 'firstname' (not just contain)
script uses an helper to check very simply if one list is sublist of another
L = [{'color':'yellow','fruit':'banana'},{'firstname':'Jack','lastname':'Black'}]
def contains_sublist(lst, sublst):
for k in sublst:
if k not in lst:
return False
return True
for di in L:
if len(di.keys()) == 2 and contains_sublist(di.keys(),
['firstname','lastname']):
print di
returns
>>>
{'lastname': 'Black', 'firstname': 'Jack'}
////////// EDIT -- thanks Martijn
You can use .viewkeys() for increased efficiency
L = [{'color':'yellow','fruit':'banana'},{'firstname':'Jack','lastname':'Black'}]
for di in L:
if len(di) == 2 and di.viewkeys() & {'firstname','lastname'}:
print di
this one checks if intersection between both sets is non empty di.viewkeys() & {'firstname','lastname'}
Related
I want to add the key 'Name' to list of dictionaries in whichever dictionary 'Name' doesn't exist.
For example,
[dict(item, **{'Name': 'apple'}) for item in d_list]
will update value of key 'Name' even if key already exists and
[dict(item, **{'Name': 'apple'}) for item in d_list if 'Name' not in item]
returns empty list
You need to handle the two different cases. In case the list is empty, and if it's not.
It's not possible to handle both use-cases in a single list comprehension statement since when the list is empty, it will always return zero-value (empty list). It is like doing for i in my_list. If the list is empty, the code inside the for block won't be executed.
I would tackle it with a single loop. I find it more readable.
>>> default = {"Name": "apple"}
>>> miss_map = {"Data": "text"}
>>> exist_map = {"Name": "pie"}
>>>
>>> d = [miss_map, exist_map]
>>>
>>> list_dict = [miss_map, exist_map]
>>> for d in list_dict:
... if "Name" not in d.keys():
... d.update(default)
...
>>> list_dict
[{'Data': 'text', 'Name': 'apple'}, {'Name': 'pie'}]
>>>
You can then move it to it's own function and pass it the list of dicts.
In one line of code:
d_list = [{**d, "Name": "apple"} for d in d_list if "Name" not in d] + [d for d in d_list if "Name" in d]
Based on Abdul Aziz comment, I could do it in 1 line using
[item.setdefault("Name", 'apple') for item in d_list]
I am searching a list of dicts to see if the code is found within one of the dictionary values, what I want to do is print out the dictionary associated to that code, if it finds it within the list.
I have the following (print statements are there to show what I want to do).
if any(d['Code'] == responsecode for d in list_of_dicts):
print(d)
the LOD would be like:
list_of_dicts = [
{'Code':'2345', 'Message':'the message'},
{'Code':'1111', 'Message':'the message'}
]
Response Code would be something like: 1111
Use a generator expression with if to filter the dicts, take its next value, use the raised exception if nothing is found as if/else replacement:
try:
print(next(d for d in list_of_dicts if d['Code'] == responsecode))
except StopIteration:
print('Not found')
Just use a list comprehension as a filter:
list_of_dicts = [{'Code':'2345', 'Message':'the message'},{'Code':'1111', 'Message':'the message'}]
responsecode = '1111'
found = [d for d in list_of_dicts if d.get('Code1',None) == responsecode]
print(found)
Output:
[{'Code': '1111', 'Message': 'the message'}]
or for responsecode = 'foo'
[]
I have a list of dictionaries as below:
l = [{'abc': 'def'}, {'ghi': 'jul'}, {'Name': 'my-name'}]
I want to get the value of the dictionary that has the key 'Name'
Initially I wrote a function as below:
def get_json_val(l, key):
for item in l:
for k, v in item.iteritems():
if k == key:
return v
But I want to do it using lambdas and filter in a single line. So, I tried as below to filter out the dictionary:
name_lst = filter(lambda item: (k == 'Name' for k in item.iteritems()), l)
It is showing the whole list again. Is there a way to get only the value (not dictionary) using lambdas and filter? Thanks in advance
Edit 1:
In the first function l is list and 'Name' is passed as key.
Seeing your initial code, this should do the same thing
>>> l = [{'abc': 'def'}, {'ghi': 'jul'}, {'Name': 'my-name'}]
>>> [i["Name"] for i in l if "Name" in i][0]
'my-name'
Your code returns only the first occurence.Using my approach, and probably also if you would use filter, you'll get (first) a list of all occurrences than only get the first (if any). This would make it less "efficient" IMHO, so I would probably change your code to be something more like this:
def get_json_val(l, key):
for item in l:
if not key in item: continue
return item[key]
Why are you iterating over your dict? That defeats the purpose. Just do
[d for d in l if key in d]
Or if you feel some compulsion to use lambda and filter:
filter(lambda d: key in d, l)
Note, these return lists instead of the corresponding value. If you want the value, you'll have to index into the list. Simply out, using a loop is probably the most reasonable approach, just don't iterate over your dict:
def f(key, l):
for d in l:
if key in d:
return d[key]
l = [{'abc': 'def'}, {'ghi': 'jul'}, {'Name': 'my-name', "no-name": "bbb"}]
I was able to do it in this way,
print filter(lambda item: item, map(lambda item: item.get("Name"), l))
if I break this down.
First check if item is in the dict or not?
fna = lambda item: item.get("Name")
then map list of dict to above function.
map(fna, l)
for above list it will print
[None, None, 'my-name']
Now we need to eliminate all the None. I am using filter with following function
fnb = lambda item: item
Now if you put all together you have your code in single line.
Here is an easy way to do it without lambdas or filter or any of that stuff. Just combine the list of dictionaries to one dictionary, then use that.
l = [{'abc': 'def'}, {'ghi': 'jul'}, {'Name': 'my-name'}]
k = {m.keys()[0] : m[m.keys()[0]] for m in l}['Name']
Note that this does not change the structure of l.
Don't underestimate the power of dict.get(). What follows is a simplification of a previous answer. In this case, there is no need for map. For the list:
l = [{'abc': 'def'}, {'ghi': 'jul'}, {'Name': 'my-name'}]
The following produces a list of dictionaries that have the key 'Name':
filtered = list(filter(lambda x: x.get('Name'), l))
From here, you can retrieve the name values for all items in the list:
values = [x['Name'] for x in filtered]
If there is only one target, as in your example, wrap this all up in one line like this:
name = list(filter(lambda x: x.get('Name'), l))[0]['Name']
i have used list comprehensions not very often but i was wondering if the below lines can be a one liner (yes the code is already small, but i am curious):
lst = ['hi', 'hello', 'bob', 'hello', 'bob', 'hello']
for index in lst:
data[index] = data.get(index,0) + 1
data would be: {'hi':1, 'hello':3, 'bob':2}
something:
d = { ... for index in lst } ????
I have tried some comprehensions but they don't work:
d = { index:key for index in lst if index in d: key = key + 1 else key = 1 }
Thanks in adv.
Simply use collections.Counter
A Counter is a dict subclass for counting hashable objects. It is an
unordered collection where elements are stored as dictionary keys and
their counts are stored as dictionary values. Counts are allowed to be
any integer value including zero or negative counts. The Counter class
is similar to bags or multisets in other languages.
import collections
l = ['hi', 'hello', 'bob', 'hello', 'bob', 'hello']
c = collections.Counter(l)
assert c['hello'] == 3
I have two dictionaries,i want to combine these two int a list in the format keys-->values,keys-->values... remove any None or ['']
currently I have the below where I can combine the dicts but not create a combined lists...i have the expecte output..
any inputs appreeciated
dict1={'313115': ['313113'], '311957': None}
dict2={'253036': [''], '305403': [], '12345': ['']}
dict = dict(dict1.items() + dict2.items())
print dict
{'313115': ['313113'], '311957': None, '253036': [''], '12345': [''], '305403': []}
EXPECTED OUTPUT:
['313115','313113','311957','253036','305403','12345']
This should do it:
[i for k, v in (dict1.items() + dict2.items()) for i in [k] + (v or []) if i]
walk the combined items of the two dicts, then walk the key plus the list of values, returning each item from the second walk that exists.
Returns ['313115', '313113', '311957', '253036', '12345', '305403'] on your example dicts -- the order is different because python's dict iteration is unordered.
EDIT:
dict.items() can be expensive on large dicts -- it takes O(n) size, rather than iterating. If you use itertools, this is more efficient (and keeps the dicts you're working with in one place):
import itertools
[i
for k, v in itertools.chain.from_iterable(d.iteritems() for d in (dict1, dict2))
for i in [k] + (v or [])
if i]
Thanks to Martijn Pieters for the from_iterable tip.
The following line gives you what you want in as efficient a manner as possible, albeit a little verbose:
from itertools import chain, ifilter
list(ifilter(None, dict1.viewkeys() | dict2.viewkeys() | set(chain(chain.from_iterable(ifilter(None, dict1.itervalues())), chain.from_iterable(ifilter(None, dict2.itervalues()))))))
You could break it down to:
values1 = chain.from_iterable(ifilter(None, dict1.itervalues()))
values2 = chain.from_iterable(ifilter(None, dict2.itervalues()))
output = list(ifilter(None, dict1.viewkeys() | dict2.viewkeys() | set(chain(values1, values2))))
ifilter with a None filter removes false-y values such as None and '' from the iterable. the outer filter is not needed for your specific input but would remove '' and None if used as keys as well. Duplicate values are removed.
Ordering in Python dictionaries is arbitrary so ordering doesn't match your sample but all expected values are there.
Demo:
>>> list(ifilter(None, dict1.viewkeys() | dict2.viewkeys() | set(chain(chain.from_iterable(ifilter(None, dict1.itervalues())), chain.from_iterable(ifilter(None, dict2.itervalues()))))))
['313115', '305403', '313113', '311957', '253036', '12345']
[each
if isinstance(each, str) else each[0]
for pair in dict(dict1, **dict2).iteritems()
for each in pair
if each not in [[''], None, []]]