根据 Python 中的当前值和下一个值更改列表中元素的顺序
Changing the order of elements in a list based on current and next value in Python
我正在尝试根据列表中的当前值和下一个值更改 Python 中列表中元素的顺序。我想使用此顺序创建一个视频播放列表,其中不包含特定类型的两个连续视频。该列表将转换为 m3u 播放列表。
我的情况:
我按以下方式命名了所有视频:“电影标题”-“发行年份”-“类型”。
例如,我不希望播放列表包含两部连续的动作片。唯一的例外应该是当我使用仅包含动作片的动作片目录时。然后就可以按照随机顺序构建播放列表了。
我目前有以下代码:
import os
import glob
from threading import Timer
cwd = os.getcwd()
# create list
videofiles = []
for file in glob.glob('**/*.mp4', recursive=True):
videofiles.append(file)
# split file on last index to compare genre
def sortSplit(file):
return file.split('- ',2)[2]
randomvideo = []
for file in videofiles:
randomvideo.append(sortSplit(file))
randomvideo.sort()
当我使用 sortSplit 函数时,我得到了我想要比较的 index/string。但是,我遇到以下问题:
- 拆分是否会“记住”原始字符串,以便在我构建列表时,它会包含完整的文件名?
- 我找不到比较列表中当前元素和下一个元素的方法
对于最后一部分,示例列表可以是:
['movie-year-ACTION', 'movie-year-ACTION', 'movie-year-SCIFI', 'movie-year-DOCUMENTARY']
列表排序应查看第一个元素中的 ACTION,将其与下一个元素进行比较,看看它们是否相同,然后将下一个元素切换为不是 ACTION 但可以是其他类型的类型.正如我预计类型的数量会增加一样,我正在寻找一种方法,例如,这些类型不会固定在另一个列表中。同样,唯一的例外是当列表中的所有元素都属于 ACTION 类型时,只需以随机顺序创建列表即可。
当然,我愿意接受完全不同的方法,只要它们能达到这个目的。
算法
forbidden_genre = None
While there are movies in the database:
Pick a movie from the non-forbidden genre which has largest remaining number of movies
Remove that movie from database, add it to playlist
forbidden_genre = genre of that movie
请注意,我们总是从剩余电影数量最多的一种类型中选择一步,以免陷入一种类型仍然有很多电影而没有足够电影的位置。可以替代的其他类型。
Python代码
为了能够按类型 select 电影,我们首先使用 itertools.groupby
按类型对电影进行分组。在下面的代码中,我们可以访问类型名称 sortedbysize_groups[genre_index][0]
和该类型的剩余电影列表 sortedbysize_groups[genre_index][1]
import operator # itemgetter(2)
import itertools # groupby
def make_playlist(videofiles):
groups_tmp = itertools.groupby(sorted(videofiles, key=operator.itemgetter(2)), operator.itemgetter(2))
sortedbysize_groups = sorted([(k, list(g)) for k,g in groups_tmp], key=lambda p: len(p[1]))
playlist = []
forbidden_genre = None
while len(sortedbysize_groups) > 1:
genre_index = get_next_nonforbidden_index(sortedbysize_groups, forbidden_genre)
next_film = sortedbysize_groups[genre_index][1].pop()
forbidden_genre = sortedbysize_groups[genre_index][0]
playlist.append(next_film)
if len(sortedbysize_groups[genre_index][1]) == 0:
sortedbysize_groups.pop(genre_index)
else:
move_back_if_necessary_to_keep_sorted(sortedbysize_groups, genre_index % len(sortedbysize_groups))
playlist.extend(sortedbysize_groups[0][1])
return playlist
def get_next_nonforbidden_index(sortedbysize_groups, forbidden_genre):
return (-1) if (sortedbysize_groups[-1][0] != forbidden_genre) else (-2)
def move_back_if_necessary_to_keep_sorted(sortedbysize_groups, i):
while i > 0 and len(sortedbysize_groups[i-1][1]) > len(sortedbysize_groups[-1][1]):
i -= 1
if i < len(sortedbysize_groups) - 1:
sortedbysize_groups[i], sortedbysize_groups[-1] = sortedbysize_groups[-1], sortedbysize_groups[i]
videofiles = [('Star Gate', 1994, 'scifi'), ('Good Will Hunting', 1997, 'drama'), ('A Beautiful Mind', 2001, 'drama'), ('Tenet', 2020, 'scifi'), ('Blade Runner', 1982, 'scifi'), ('The Tree of Life', 2011, 'experimental'), ('Pi', 1998, 'experimental')]
print(make_playlist(videofiles))
# [('Blade Runner', 1982, 'scifi'), ('Pi', 1998, 'experimental'), ('A Beautiful Mind', 2001, 'drama'), ('Tenet', 2020, 'scifi'), ('Good Will Hunting', 1997, 'drama'), ('Star Gate', 1994, 'scifi'), ('The Tree of Life', 2011, 'experimental')]
如果由于一种类型的电影太多而没有完美的解决方案,算法将尽力而为,播放列表将以该类型的两部或更多电影结束。请注意,只有超过一半的电影属于同一类型时才会发生这种情况。
我正在尝试根据列表中的当前值和下一个值更改 Python 中列表中元素的顺序。我想使用此顺序创建一个视频播放列表,其中不包含特定类型的两个连续视频。该列表将转换为 m3u 播放列表。
我的情况: 我按以下方式命名了所有视频:“电影标题”-“发行年份”-“类型”。
例如,我不希望播放列表包含两部连续的动作片。唯一的例外应该是当我使用仅包含动作片的动作片目录时。然后就可以按照随机顺序构建播放列表了。
我目前有以下代码:
import os
import glob
from threading import Timer
cwd = os.getcwd()
# create list
videofiles = []
for file in glob.glob('**/*.mp4', recursive=True):
videofiles.append(file)
# split file on last index to compare genre
def sortSplit(file):
return file.split('- ',2)[2]
randomvideo = []
for file in videofiles:
randomvideo.append(sortSplit(file))
randomvideo.sort()
当我使用 sortSplit 函数时,我得到了我想要比较的 index/string。但是,我遇到以下问题:
- 拆分是否会“记住”原始字符串,以便在我构建列表时,它会包含完整的文件名?
- 我找不到比较列表中当前元素和下一个元素的方法
对于最后一部分,示例列表可以是:
['movie-year-ACTION', 'movie-year-ACTION', 'movie-year-SCIFI', 'movie-year-DOCUMENTARY']
列表排序应查看第一个元素中的 ACTION,将其与下一个元素进行比较,看看它们是否相同,然后将下一个元素切换为不是 ACTION 但可以是其他类型的类型.正如我预计类型的数量会增加一样,我正在寻找一种方法,例如,这些类型不会固定在另一个列表中。同样,唯一的例外是当列表中的所有元素都属于 ACTION 类型时,只需以随机顺序创建列表即可。
当然,我愿意接受完全不同的方法,只要它们能达到这个目的。
算法
forbidden_genre = None
While there are movies in the database:
Pick a movie from the non-forbidden genre which has largest remaining number of movies
Remove that movie from database, add it to playlist
forbidden_genre = genre of that movie
请注意,我们总是从剩余电影数量最多的一种类型中选择一步,以免陷入一种类型仍然有很多电影而没有足够电影的位置。可以替代的其他类型。
Python代码
为了能够按类型 select 电影,我们首先使用 itertools.groupby
按类型对电影进行分组。在下面的代码中,我们可以访问类型名称 sortedbysize_groups[genre_index][0]
和该类型的剩余电影列表 sortedbysize_groups[genre_index][1]
import operator # itemgetter(2)
import itertools # groupby
def make_playlist(videofiles):
groups_tmp = itertools.groupby(sorted(videofiles, key=operator.itemgetter(2)), operator.itemgetter(2))
sortedbysize_groups = sorted([(k, list(g)) for k,g in groups_tmp], key=lambda p: len(p[1]))
playlist = []
forbidden_genre = None
while len(sortedbysize_groups) > 1:
genre_index = get_next_nonforbidden_index(sortedbysize_groups, forbidden_genre)
next_film = sortedbysize_groups[genre_index][1].pop()
forbidden_genre = sortedbysize_groups[genre_index][0]
playlist.append(next_film)
if len(sortedbysize_groups[genre_index][1]) == 0:
sortedbysize_groups.pop(genre_index)
else:
move_back_if_necessary_to_keep_sorted(sortedbysize_groups, genre_index % len(sortedbysize_groups))
playlist.extend(sortedbysize_groups[0][1])
return playlist
def get_next_nonforbidden_index(sortedbysize_groups, forbidden_genre):
return (-1) if (sortedbysize_groups[-1][0] != forbidden_genre) else (-2)
def move_back_if_necessary_to_keep_sorted(sortedbysize_groups, i):
while i > 0 and len(sortedbysize_groups[i-1][1]) > len(sortedbysize_groups[-1][1]):
i -= 1
if i < len(sortedbysize_groups) - 1:
sortedbysize_groups[i], sortedbysize_groups[-1] = sortedbysize_groups[-1], sortedbysize_groups[i]
videofiles = [('Star Gate', 1994, 'scifi'), ('Good Will Hunting', 1997, 'drama'), ('A Beautiful Mind', 2001, 'drama'), ('Tenet', 2020, 'scifi'), ('Blade Runner', 1982, 'scifi'), ('The Tree of Life', 2011, 'experimental'), ('Pi', 1998, 'experimental')]
print(make_playlist(videofiles))
# [('Blade Runner', 1982, 'scifi'), ('Pi', 1998, 'experimental'), ('A Beautiful Mind', 2001, 'drama'), ('Tenet', 2020, 'scifi'), ('Good Will Hunting', 1997, 'drama'), ('Star Gate', 1994, 'scifi'), ('The Tree of Life', 2011, 'experimental')]
如果由于一种类型的电影太多而没有完美的解决方案,算法将尽力而为,播放列表将以该类型的两部或更多电影结束。请注意,只有超过一半的电影属于同一类型时才会发生这种情况。