Sorting a dict...efficient way to do it? - python

I've have the problem where I have a dict of the passengers like this:
passengers = {
1: {'name': 'Foo', 'lastname': 'Bar', 'exclusive': True},
2: {'name': 'John', 'lastname': 'Doe'},
3: {'name': 'Rocky', 'lastname': 'Balboa', 'exclusive': True},
4: {'name': 'Mohammed', 'lastname': 'Smith'}
}
And I need to print the results like this items with exclusive first then the rest:
THIS IS THE DESIRED OUTPUT
List of passengers:
===================
1.- Foo Bar
2.- Rocky Balboa
3.- John Doe
4.- Mohammed Smith
I tried with collections.deque, and I haven't found anything that works for me, until I came up with this function:
def prioritize_passengers(dictionary):
priority_list = []
normal_list = []
sorted_list = []
for key, item in dictionary.iteritems():
if 'exclusive' in item:
priority_list.append(key)
else:
normal_list.append(key)
sorted_list = priority_list + normal_list
return sorted_list
And then I use it on my data like this:
# Assuming passenger is the same var as above
sorted_list = prioritize_passengers(passengers)
print "List of passengers:\n==================="
for elem in sorted_list:
passenger = passengers[elem]
print "{} {}".format(passenger['name'], passenger['lastname']
Is that the only way to do it or is there a more clear/efficient way to achieve it? Again, the second paragraph is the desired output.

Yes, there are other ways to sort that list. Here is one:
passengers = {
1: {'name': 'Foo', 'lastname': 'Bar', 'exclusive': True},
2: {'name': 'John', 'lastname': 'Doe'},
3: {'name': 'Rocky', 'lastname': 'Balboa', 'exclusive': True},
4: {'name': 'Mohammed', 'lastname': 'Smith'}
}
list_of_passengers = sorted(
passengers.items(),
key=lambda x: (('exclusive' not in x[1]), x[0]))
for i, (_, passenger) in enumerate(list_of_passengers, 1):
print '{}. - {} {}'.format(i, passenger['name'], passenger['lastname'])
Since you don't care about the order other than the exclusive-ness, then you this might work for you:
passengers = {
1: {'name': 'Foo', 'lastname': 'Bar', 'exclusive': True},
2: {'name': 'John', 'lastname': 'Doe'},
3: {'name': 'Rocky', 'lastname': 'Balboa', 'exclusive': True},
4: {'name': 'Mohammed', 'lastname': 'Smith'}
}
list_of_passengers = sorted(
passengers.values(), key=lambda x: 'exclusive' not in x)
for i, passenger in enumerate(list_of_passengers, 1):
print '{}. - {} {}'.format(i, passenger['name'], passenger['lastname'])
Finally, if what you really want to do is to create two separate lists, you can use the filter() builtin funciton:
upper_crust = filter(lambda x: 'exclusive' in x, passengers.values())
riff_raff = filter(lambda x: 'exclusive' not in x, passengers.values())

Related

List of dictionary need to split and take first only value after split

I have list of dictionary in Python that i want to convert to another list after i split the value in the dictionary and take only first value after the split.
myList = [
{'name': 'CN=John,Li',
'type': 'Agent',
'isMember': False},
{'name': 'CN=Moses,Somewhere',
'type': 'Worker',
'isMember': True},
{'name': 'CN=David, Something',
'type': 'Agent',
'isMember': False}
]
required_fields = ['name', 'isMember']
I'm looking for desired output after splitting the values
myList = [
{'name': 'John',
'isMember': False},
{'name': 'Moses',
'isMember': True},
{'name': 'David',
'isMember': False}
]
Thank you in advance
A small helper function to find the CN=... part from the name, and a list comprehension to loop over myList:
import re
def get_cn(name):
return re.search("CN=([^,]+)", name).group(1)
my_list = [...]
new_list = [
{'name': get_cn(x['name']), 'isMember': x['isMember']}
for x
in my_list
]
print(new_list)
This outputs
[{'name': 'John', 'isMember': False}, {'name': 'Moses', 'isMember': True}, {'name': 'David', 'isMember': False}]
This can be done in single liner -->
new_list = []
for i in range(len(myList)):
new_list.append({"name": re.search("CN=([^,]+)", myList[i]['name']).group(1), \
"isMember":myList[i]['isMember']})
print(new_list)

understanding nested python dict comprehension

I am getting along with dict comprehensions and trying to understand how the below 2 dict comprehensions work:
select_vals = ['name', 'pay']
test_dict = {'data': [{'name': 'John', 'city': 'NYC', 'pay': 70000}, {'name': 'Mike', 'city': 'NYC', 'pay': 80000}, {'name': 'Kate', 'city': 'Houston', 'pay': 65000}]}
dict_comp1 = [{key: item[key] for key in select_vals } for item in test_dict['data'] if item['pay'] > 65000 ]
The above line gets me
[{'name': 'John', 'pay': 70000}, {'name': 'Mike', 'pay': 80000}]
dict_comp2 = [{key: item[key]} for key in select_vals for item in test_dict['data'] if item['pay'] > 65000 ]
The above line gets me
[{'name': 'John'}, {'name': 'Mike'}, {'pay': 70000}, {'pay': 80000}]
How does the two o/ps vary when written in a for loop ? When I execute in a for loop
dict_comp3 = []
for key in select_vals:
for item in test_dict['data']:
if item['pay'] > 65000:
dict_comp3.append({key: item[key]})
print(dict_comp3)
The above line gets me same as dict_comp2
[{'name': 'John'}, {'name': 'Mike'}, {'pay': 70000}, {'pay': 80000}]
How do I get the o/p as dict_comp1 in a for loop ?
The select vals iteration should be the inner one
result = []
for item in test_dict['data']:
if item['pay'] > 65000:
aux = {}
for key in select_vals:
aux[key] = item[key]
result.append(aux)

Build a dictionary with single elements or lists as values

I have a list of dictionaries:
mydict = [
{'name': 'test1', 'value': '1_1'},
{'name': 'test2', 'value': '2_1'},
{'name': 'test1', 'value': '1_2'},
{'name': 'test1', 'value': '1_3'},
{'name': 'test3', 'value': '3_1'},
{'name': 'test4', 'value': '4_1'},
{'name': 'test4', 'value': '4_2'},
]
I would like to use it to create a dictionary where the values are lists or single values depending of number of their occurrences in the list above.
Expected output:
outputdict = {
'test1': ['1_1', '1_2', '1_3'],
'test2': '2_1',
'test3': '3_1',
'test4': ['4_1', '4_2'],
}
I tried to do it the way below but it always returns a list, even when there is just one value element.
outputdict = {}
outputdict.setdefault(mydict.get('name'), []).append(mydict.get('value'))
The current output is:
outputdict = {
'test1': ['1_1', '1_2', '1_3'],
'test2': ['2_1'],
'test3': ['3_1'],
'test4': ['4_1', '4_2'],
}
Do what you have already done, and then convert single-element lists afterwards:
outputdict = {
name: (value if len(value) > 1 else value[0])
for name, value in outputdict.items()
}
You can use a couple of the built-in functions mainly itertools.groupby:
from itertools import groupby
from operator import itemgetter
mydict = [
{'name': 'test1', 'value': '1_1'},
{'name': 'test2', 'value': '2_1'},
{'name': 'test1', 'value': '1_2'},
{'name': 'test1', 'value': '1_3'},
{'name': 'test3', 'value': '3_1'},
{'name': 'test4', 'value': '4_1'},
{'name': 'test4', 'value': '4_2'},
]
def keyFunc(x):
return x['name']
outputdict = {}
# groupby groups all the items that matches the returned value from keyFunc
# in our case it will use the names
for name, groups in groupby(mydict, keyFunc):
# groups will contains an iterator of all the items that have the matched name
values = list(map(itemgetter('value'), groups))
if len(values) == 1:
outputdict[name] = values[0]
else:
outputdict[name] = values
print(outputdict)

Mapping names in Python

I have been given the following list of dictionaries:
names = [
{'first_name': 'Jane', 'last_name': 'Doe'},
{'first_name': 'John', 'last_name': 'Kennedy'},
{'first_name': 'Ada', 'last_name': 'Lovelace'}
]
Part a was to return an array of full names, which I did as follows:
[user['first_name'] +' '+ user['last_name'] for user in names]
It returned the following:
['Jane Doe', 'John Kennedy', 'Ada Lovelace']
Part b is to Now do the same thing above, only return a list of dictionaries, with 'name' being the key. Result should be:
python
[{'name':'Jane Doe'},{'name':'John Kennedy'},{'name': 'Ada Lovelace'}]
I have tried everything I can think of. From trying to change the key, to changing back to a list and then back to a dictionary. I'm very new at Python and would appreciate any help possible.
[{'name': '{first_name} {last_name}'.format(**n)} for n in names]
The following comprehension using join will work:
result = [{'name': ' '.join((d['first_name'], d['last_name']))} for d in names]
# [{'name': 'Jane Doe'}, {'name': 'John Kennedy'}, {'name': 'Ada Lovelace'}]
Adjust your list comprehension to the following:
names = [
{'first_name': 'Jane', 'last_name': 'Doe'},
{'first_name': 'John', 'last_name': 'Kennedy'},
{'first_name': 'Ada', 'last_name': 'Lovelace'}
]
result = [{'name':d['first_name']+' '+ d['last_name']} for d in names]
print(result)
The output:
[{'name': 'Jane Doe'}, {'name': 'John Kennedy'}, {'name': 'Ada Lovelace'}]
list(map(lambda d: {'name': ' '.join((d['first_name'], d['last_name']))},names))

Sorting a list of dictionaries by all keys being unique

Pulling my hair out with this one.
I have a list of dictionaries without a unique primary ID key for each unique entry (the dictionary is built on the fly):
dicts = [{'firstname': 'john', 'lastname': 'doe', 'code': 'crumpets'},
{'firstname': 'john', 'lastname': 'roe', 'code': 'roe'},
{'firstname': 'john', 'lastname': 'doe', 'code': 'crumpets'},
{'firstname': 'thom', 'lastname': 'doe', 'code': 'crumpets'},
]
How do I go about filtering out lists of dictionaries like this where any repeating {} within the list are removed? So I need to check if all three of the dictionary keys match up with another in the list...and then discard that from the dict if that check is met.
So, for my example above, the first and third "entries" need to be removed as they are duplicates.
You use create frozensets from the dicts and put those in a set to remove dupes:
dcts = [dict(d) for d in set(frozenset(d.items()) for d in dcts)]
print(dcts)
[{'code': 'roe', 'firstname': 'john', 'lastname': 'roe'},
{'code': 'crumpets', 'firstname': 'thom', 'lastname': 'doe'},
{'code': 'crumpets', 'firstname': 'john', 'lastname': 'doe'}]
If you choose to remove all entries of the duplicates you can use a counter:
from collections import Counter
dcts = [dict(d) for d, cnt in Counter(frozenset(d.items()) for d in dcts).items()
if cnt==1]
print(dcts)
[{'code': 'roe', 'firstname': 'john', 'lastname': 'roe'},
{'code': 'crumpets', 'firstname': 'thom', 'lastname': 'doe'}]
Remove duplicates in a list of non-hashable elements requires you to make them hashable on the fly:
def remove_duplicated_dicts(elements):
seen = set()
result = []
for element in elements:
element_as_tuple = tuple(element.items())
if element_as_tuple not in seen:
seen.add(element_as_tuple)
result.append(element)
return result
d = [{'firstname': 'john', 'lastname': 'doe', 'code': "crumpets"},
{'firstname': 'john', 'lastname': 'roe', 'code': "roe"},
{'firstname': 'john', 'lastname': 'doe', 'code': "crumpets"},
{'firstname': 'thom', 'lastname': 'doe', 'code': "crumpets"},
]
print(remove_duplicated_dicts(d))
PS.
Non-obvious differences with the accepted answer of Moses Koledoye (as of 2017-06-19 at 13:00:00):
preservation of the original list order;
faster conversions: dict -> tuple instead of dict -> frozendict -> dict (take it with a grain of salt: I have made no benchmark).
Given the values of the dictionary are hashable, we can generate our own uniqness filter:
def uniq(iterable, key = lambda x:x):
keys = set()
for item in iterable:
ky = key(item)
if ky not in keys:
yield item
keys.add(ky)
We can then simply use the filter, like:
list(uniq(dicts,key=lambda x:(x['firstname'],x['lastname'],x['code'])))
The filter maintains the original order, and will - for this example - generate:
>>> list(uniq(dicts,key=lambda x:(x['firstname'],x['lastname'],x['code'])))
[{'code': 'crumpets', 'firstname': 'john', 'lastname': 'doe'},
{'code': 'roe', 'firstname': 'john', 'lastname': 'roe'},
{'code': 'crumpets', 'firstname': 'thom', 'lastname': 'doe'}]

Categories