根据 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')]

如果由于一种类型的电影太多而没有完美的解决方案,算法将尽力而为,播放列表将以该类型的两部或更多电影结束。请注意,只有超过一半的电影属于同一类型时才会发生这种情况。