I am trying to sort a dictionary by value, which is a timestamp in the format H:MM:SS (eg "0:41:42") but the code below doesn't work as expected:
album_len = {
'The Piper At The Gates Of Dawn': '0:41:50',
'A Saucerful of Secrets': '0:39:23',
'More': '0:44:53', 'Division Bell': '1:05:52',
'The Wall': '1:17:46',
'Dark side of the moon': '0:45:18',
'Wish you were here': '0:44:17',
'Animals': '0:41:42'
}
album_len = OrderedDict(sorted(album_len.items()))
This is the output I get:
OrderedDict([
('A Saucerful of Secrets', '0:39:23'),
('Animals', '0:41:42'),
('Dark side of the moon', '0:45:18'),
('Division Bell', '1:05:52'),
('More', '0:44:53'),
('The Piper At The Gates Of Dawn', '0:41:50'),
('The Wall', '1:17:46'),
('Wish you were here', '0:44:17')])
It's not supposed to be like that. The first element I expected to see is ('The Wall', '1:17:46'), the longest one.
How do I get the elements sorted the way I intended?
Try converting each value to a datetime and using that as the key:
from collections import OrderedDict
from datetime import datetime
def convert_to_datetime(val):
return datetime.strptime(val, "%H:%M:%S")
album_len = {'The Piper At The Gates Of Dawn': '0:41:50',
'A Saucerful of Secrets': '0:39:23', 'More': '0:44:53',
'Division Bell': '1:05:52', 'The Wall': '1:17:46',
'Dark side of the moon': '0:45:18',
'Wish you were here': '0:44:17', 'Animals': '0:41:42'}
album_len = OrderedDict(
sorted(album_len.items(), key=lambda i: convert_to_datetime(i[1]))
)
print(album_len)
Output:
OrderedDict([('A Saucerful of Secrets', '0:39:23'), ('Animals', '0:41:42'),
('The Piper At The Gates Of Dawn', '0:41:50'),
('Wish you were here', '0:44:17'), ('More', '0:44:53'),
('Dark side of the moon', '0:45:18'), ('Division Bell', '1:05:52'),
('The Wall', '1:17:46')])
Or in descending order with reverse set to True:
album_len = OrderedDict(
sorted(
album_len.items(),
key=lambda i: convert_to_datetime(i[1]),
reverse=True
)
)
Output:
OrderedDict([('The Wall', '1:17:46'), ('Division Bell', '1:05:52'),
('Dark side of the moon', '0:45:18'), ('More', '0:44:53'),
('Wish you were here', '0:44:17'),
('The Piper At The Gates Of Dawn', '0:41:50'),
('Animals', '0:41:42'), ('A Saucerful of Secrets', '0:39:23')])
Edit: If only insertion order needs maintained and the OrderedDict specific functions like move_to_end are not going to be used then a regular python dict also works here for Python3.7+.
Ascending:
album_len = dict(
sorted(album_len.items(), key=lambda i: convert_to_datetime(i[1]))
)
Descending:
album_len = dict(
sorted(album_len.items(), key=lambda i: convert_to_datetime(i[1]),
reverse=True)
)
This is a duplicate of the question: How do I sort a dictionary by value?"
>>> dict(sorted(album_len.items(), key=lambda item: item[1]))
{'A Saucerful of Secrets': '0:39:23',
'Animals': '0:41:42',
'The Piper At The Gates Of Dawn': '0:41:50',
'Wish you were here': '0:44:17',
'More': '0:44:53',
'Dark side of the moon': '0:45:18',
'Division Bell': '1:05:52',
'The Wall': '1:17:46'}
Note: the time format is already lexicographically ordered, you don't need to convert to datetime.
See comment below of #DarrylG. He's totally right, therefore, the remark on the lexicographic order is valid as long as the duration does not exceed 9:59:59 except if hours are padded with a leading zero.
Related
I have this homework problem, and I'm new to python. I have this list of tuples:
[('the, this is me', 'the night'), ('the night', 'me'), ('me', 'the store')]
My code doesn't work when I'm trying to write to target_bigrams with only the tuples that have "the" in position [0]. Please help.
target_bigrams = ()
bigrams_length = len(bigrams)
for i in range(bigrams_length):
if i[0] == target_word:
target_bigrams.append(i[0])
I think this is what you need;
bigrams = [('the, this is me', 'the night'), ('the night', 'me'), ('me', 'the store')]
target_word = 'the'
target_bigrams = []
bigrams_length = len(bigrams)
for i in range(bigrams_length):
if bigrams[i][0].startswith(target_word):
target_bigrams.append(bigrams[i][0])
The question is not clear, but i believe that you want to separate the tuples which has "the" in the first position.
If that is the case, here is the sample code for your reference
lst = [('the, this is me', 'the night'), ('the night', 'me'), ('me', 'the store'), ('the', 'the store'),("the","How are you")]
target_list = []
target_word = "the"
for i in range(len(lst)):
if target_word == lst[i][0]:
target_list.append(lst[i])
for i in target_list:
print(i)
Output:
('the', 'the store')
('the', 'How are you')
I am new to programming and python and I dont know how to solve this problem.
my_dict = {'tiger': ['claws', 'sharp teeth', 'four legs', 'stripes'],
'elephant': ['trunk', 'four legs', 'big ears', 'gray skin'],
'human': ['two legs', 'funny looking ears', 'a sense of humor']
}
new_dict = {}
for k, v in my_dict.items():
new_v = v + "WOW"
new_dict[k] = new_v
print(new_dict)
I want to make a new dictionary with added phrase but I got an error "can only concatenate list (not "str") to list", but when I am using only one value per key the programme works. Is there any solution to this?
You can concatenate a list to another list as follows:
if __name__ == '__main__':
my_dict = {'tiger': ['claws', 'sharp teeth', 'four legs', 'stripes'],
'elephant': ['trunk', 'four legs', 'big ears', 'gray skin'],
'human': ['two legs', 'funny looking ears', 'a sense of humor']
}
new_dict = {}
for k, v in my_dict.items():
new_v = v + ["WOW"]
new_dict[k] = new_v
print(new_dict)
{'tiger': ['claws', 'sharp teeth', 'four legs', 'stripes', 'WOW'], 'elephant': ['trunk', 'four legs', 'big ears', 'gray skin', 'WOW'], 'human': ['two legs', 'funny looking ears', 'a sense of humor', 'WOW']}
With list, you only can concatenate a list only.
#So here you're trying to concatenate a list with the string
my_dict['tiger'] + 'WOW' # this won't work as one is string and other is List.
my_dict['tiger'] + ['WOW'] # this will work as both are of same type and concatenation will happen.
I've made this list; each item is a string that contains commas (in some cases) and colon (always):
dinner = [
'cake,peas,cheese : No',
'duck,broccoli,onions : Maybe',
'motor oil : Definitely Not',
'pizza : Damn Right',
'ice cream : Maybe',
'bologna : No',
'potatoes,bacon,carrots,water: Yes',
'rats,hats : Definitely Not',
'seltzer : Yes',
'sleeping,whining,spitting : No Way',
'marmalade : No'
]
I would like to create a new list from the one above as follows:
['cake : No',
'peas : No',
'cheese : No',
'duck : Maybe',
'broccoli : Maybe',
'onions : Maybe',
'motor oil : Definitely Not',
'pizza : Damn Right',
'ice cream : Maybe',
'bologna : No',
'potatoes : Yes',
'bacon : Yes',
'carrots : Yes',
'water : Yes',
'rats : Definitely Not',
'hats : Definitely Not',
'seltzer : Yes',
'sleeping : No Way',
'whining : No Way',
'spitting : No Way',
'marmalade : No']
But I'd like to know if/ how it's possible to do so in a line or two of efficient code employing primarily Python's higher order functions. I've been attempting it:
reduce(lambda x,y: x + y, (map(lambda x: x.split(':')[0].strip().split(','), dinner)))
...produces this:
['cake',
'peas',
'cheese',
'duck',
'broccoli',
'onions',
'motor oil',
'pizza',
'ice cream',
'bologna',
'potatoes',
'bacon',
'carrots',
'water',
'rats',
'hats',
'seltzer',
'sleeping',
'whining',
'spitting',
'marmalade']
...but I'm struggling with appending the piece of each string after the colon back onto each item.
I would create a dict using, zip, map and itertools.repeat:
from itertools import repeat
data = ({k.strip(): v.strip() for _k, _v in map(lambda x: x.split(":"), dinner)
for k, v in zip(_k.split(","), repeat(_v))})
from pprint import pprint as pp
pp(data)
Output:
{'bacon': 'Yes',
'bologna': 'No',
'broccoli': 'Maybe',
'cake': 'No',
'carrots': 'Yes',
'cheese': 'No',
'duck': 'Maybe',
'hats': 'Definitely Not',
'ice cream': 'Maybe',
'marmalade': 'No',
'motor oil': 'Definitely Not',
'onions': 'Maybe',
'peas': 'No',
'pizza': 'Damn Right',
'potatoes': 'Yes',
'rats': 'Definitely Not',
'seltzer': 'Yes',
'sleeping': 'No Way',
'spitting': 'No Way',
'water': 'Yes',
'whining': 'No Way'}
Or using the dict constructor:
from itertools import repeat
data = dict(map(str.strip, t) for _k, _v in map(lambda x: x.split(":"), dinner)
for t in zip(_k.split(","), repeat(_v)))
from pprint import pprint as pp
pp(data)
If you really want a list of strings, we can do something similar using itertools.chain and joining the substrings:
from itertools import repeat, chain
data = chain.from_iterable(map(":".join, zip(_k.split(","), repeat(_v)))
for _k, _v in map(lambda x: x.split(":"), dinner))
from pprint import pprint as pp
pp(list(data))
Output:
['cake: No',
'peas: No',
'cheese : No',
'duck: Maybe',
'broccoli: Maybe',
'onions : Maybe',
'motor oil : Definitely Not',
'pizza : Damn Right',
'ice cream : Maybe',
'bologna : No',
'potatoes: Yes',
'bacon: Yes',
'carrots: Yes',
'water: Yes',
'rats: Definitely Not',
'hats : Definitely Not',
'seltzer : Yes',
'sleeping: No Way',
'whining: No Way',
'spitting : No Way',
'marmalade : No']
Assuming you really need it as a list of strings vs. a dictionary, which looks like a better data structure.
By simplify using comprehensions you can do this:
>>> [[x+':'+y for x in i.split(',')]
... for i, y in map(lambda l: map(str.strip, l.split(':')), dinner)]
[['cake:No', 'peas:No', 'cheese:No'],
['duck:Maybe', 'broccoli:Maybe', 'onions:Maybe'],
['motor oil:Definitely Not'],
...
['marmalade:No']]
Now just add up the lists:
>>> from operator import add
>>> reduce(add, ([x+':'+y for x in i.split(',')]
... for i, y in map(lambda l: map(str.strip, l.split(':')), dinner)), [])
['cake:No',
'peas:No',
'cheese:No',
'duck:Maybe',
...
'marmalade:No']
Or just flatten the list:
>>> [a for i, y in map(lambda l: map(str.strip, l.split(':')), dinner)
... for a in (x+':'+y for x in i.split(','))]
['cake:No',
'peas:No',
'cheese:No',
'duck:Maybe',
...
'marmalade:No']
This may work:
def processList (aList):
finalList = []
for aListEntry in aList:
aListEntry_entries = aListEntry.split(':')
aListEntry_list = aListEntry_entries[0].split(',')
for aListEntry_list_entry in aListEntry_list:
finalList.append(aListEntry_list_entry.strip() + ' : ' + aListEntry_entries[1].strip())
return finalList
List comprehensions are preferred in Python (check eg this), due to better legibility (at least for some;).
The code demonstrates two types of list comprehension nesting, the first is basically chaining the operations, the other produces one list from two nested loops.
If you make your data more consistent by adding one space after the carrots, water, you can get rid of two .strip() calls;)
dinner = [
'cake,peas,cheese : No',
'duck,broccoli,onions : Maybe',
'motor oil : Definitely Not',
'pizza : Damn Right',
'ice cream : Maybe',
'bologna : No',
'potatoes,bacon,carrots,water : Yes',
'rats,hats : Definitely Not',
'seltzer : Yes',
'sleeping,whining,spitting : No Way',
'marmalade : No'
]
prefs = [(pref, items.split(',')) for items, pref in [it.split(" : ") for it in dinner]]
[" : ".join([item, pref]) for pref, items in prefs for item in items]
I have a list
key_list = ['m.title', 'm.studio', 'm.gross', 'm.year']
cols = [
['Titanic', 'The Lord of the Rings: The Return of the King', 'Toy Story 3'],
['Par.', 'NL', 'BV'],
['2186.8', '1119.9', '1063.2'],
['1997', '2003', '2010']
]
I want to construct a dictionary table_dict whose keys are the elements of key_list, and values are respective sublists of cols.
My current code is as follows:
i = 0
for key in key_list:
table_dict[key] = cols[i]
i = i + 1
return table_dict
I can't seem to find an error, yet when I run it I get:
dict[key] = cols[i]
IndexError: list index out of range
You can simply zip the keys and values and pass it to the dict. You can read more about constructing dictionaries here
print dict(zip(key_list, cols))
Output
{'m.gross': ['2186.8', '1119.9', '1063.2'], 'm.studio': ['Par.', 'NL', 'BV'], 'm.year': ['1997', '2003', '2010'], 'm.title': ['Titanic', 'The Lord of the Rings: The Return of the King', 'Toy Story 3']}
key_list = ['m.title', 'm.studio', 'm.gross', 'm.year']
cols = [
['Titanic', 'The Lord of the Rings: The Return of the King', 'Toy Story 3'],
['Par.', 'NL', 'BV'],
['2186.8', '1119.9', '1063.2'],
['1997', '2003', '2010']]
for i in cols:
print dict(zip(key_list, i))
If You want OUTPUT like this
{'m.gross': 'Toy Story 3', 'm.studio': 'The Lord of the Rings: The Return of the King','m.title': 'Titanic'}{'m.gross': 'BV', 'm.studio': 'NL', 'm.title': 'Par.'}{'m.gross': '1063.2', 'm.studio': '1119.9', 'm.title': '2186.8'}{'m.gross': '2010', 'm.studio': '2003','m.title': '1997'}
The example you provided works without an error. There might be another problem within your code. However, what the error message tells you is that,
The index i of list cols is out of bounds. Which means while iterating over the first list (which has 4 elements in it, so iterating 4 times) the other list cols does not have enough items - meaning less than 4 probably.
The work around this issue refer to the python docs dict
table_dict = dict(zip(key_list, cols))
print table_dict
Output:
{'m.gross': ['2186.8', '1119.9', '1063.2'], 'm.studio': ['Par.', 'NL', 'BV'], 'm.year': ['1997', '2003', '2010'], 'm.title': ['Titanic', 'The Lord of the Rings: The Return of the King', 'Toy Story 3']}
For example i would have a list of of
lists = ['jack 20', 'ben 10', 'alisdar 50', 'ollie 35']
and I would need to sort it so based on the number,
lists.sort() = ['ben 10', 'jack 20', 'ollie 35', 'alisdar 50']
Possible somehow use formatting with split()?
Use a key function:
lists.sort(key=lambda s: int(s.rsplit(None, 1)[-1]))
The key callable is passed each and every element in lists and that element is sorted according to the return value. In this case we
split once on whitespace, starting on the right
take the last element of the split
turn that into an integer
The argument to key can be any callable, but a lambda is just more compact. You can try it out in the command prompt:
>>> key_function = lambda s: int(s.rsplit(None, 1)[-1])
>>> key_function('ben 10')
10
>>> key_function('Guido van Rossum 42')
42
In effect, when sorting the values are augmented with the return value of that function, and what is sorted is:
[(20, 0, 'jack 20'), (10, 1, 'ben 10'), (50, 2, 'alisdar 50'), (35, 3, 'ollie 35')]
instead (with the second value, the index of the element, added to keep the original order in case of equal sort keys).
Result:
>>> lists = ['jack 20', 'ben 10', 'alisdar 50', 'ollie 35']
>>> lists.sort(key=lambda s: int(s.rsplit(None, 1)[-1]))
>>> lists
['ben 10', 'jack 20', 'ollie 35', 'alisdar 50']
Use a key function that does what you want:
lists.sort(key=lambda e: int(e.split()[1]))
If some of your items don't follow that format, you'll have to write something a little more elaborate.
It would be better if you had a more appropriate data type than a string to represent, say, a person's name and age. One way would be a dictionary:
lists = ['jack 20', 'ben 10', 'alisdar 50', 'ollie 35']
d = dict(item.split(' ') for item in lists)
This constructs a dictionary from a stream of two-element lists.
Then you can sort like this:
print sorted((v, k) for k, v in d.iteritems())
and get this:
>>> lists = ['jack 20', 'ben 10', 'alisdar 50', 'ollie 35']
>>> d = dict(item.split(' ') for item in lists)
>>> print sorted((v, k) for k, v in d.iteritems())
[('10', 'ben'), ('20', 'jack'), ('35', 'ollie'), ('50', 'alisdar')]
Or you could convert age to integer:
>>> lists = ['jack 20', 'ben 10', 'alisdar 50', 'ollie 35']
>>> person_iter = (item.split(' ') for item in lists)
>>> d = {k: int(v) for k, v in person_iter}
>>> print sorted((v, k) for k, v in d.iteritems())
[(10, 'ben'), (20, 'jack'), (35, 'ollie'), (50, 'alisdar')]
person_iter is a generator that produces pairs of name-age. You feed that to the dictionary comprehension and convert the second argument to an integer.
The basic idea, though, is that you will have an easier time if you use more precise data types for your purposes.