将 objects 的 Python 列表转换为 one-to-many objects 的列表

Turning a Python list of objects into a list of one-to-many objects

假设我们有一个图书列表 objects,每本书都有一个标题和一个作者 ID:

books = [
    { 'title': 'book1', 'author_id': 'author1' },
    { 'title': 'book2', 'author_id': 'author2' },
    { 'title': 'book3', 'author_id': 'author1' }
]

我们如何有效地将此列表转换为作者 objects 的列表,其中一本书 属性 包含该作者的所有书籍?即,将那个列表变成这个列表:

authors = [
    { 'author_id': 'author1', 'books': [{ 'title': 'book1' }, { 'title': 'book3' }],
    { 'author_id': 'author2', 'books': [{ 'title': 'book2' }]
]

这是我的解决方案尝试,尽管它看起来效率低下且令人费解:

authors = []

for book in books:
    # Index of the author's object if it has already been added to the array
    existing_author_indices = [i for i in range(len(authors)) if authors[i]['author_id'] == book['author_id']]

    # The author is already in authors, so add the book to its books
    if len(existing_author_indices) > 0:
        authors[existing_author_indices[0]]['books'].append(book)
    # Add the author to authors with this book as the only one yet
    else:
        author = { 'author_id': book['author_id'], 'books': [book] }

如有任何建议,我们将不胜感激。

我会建议这个(编辑你认为合适的方式,我只做了标题)

{'author1': ['book1', 'book3'], 'author2': ['book2']}

这样就可以了

authors = dict()
for book in books:
    author_id = book['author_id']
    if author_id not in authors:
        authors[author_id] = list()
    author_books = authors[author_id]
    book_title = book['title']
    if book_title not in author_books:
        author_books.append(book_title)

您可以使用 defaultdict 生成一个字典,其中键是作者姓名,值是每个作者的书籍列表。一旦你有了它,就很容易转换成列表:

from collections import defaultdict

books = [
    { 'title': 'book1', 'author_id': 'author1' },
    { 'title': 'book2', 'author_id': 'author2' },
    { 'title': 'book3', 'author_id': 'author1' }
]

d = defaultdict(list)
for book in books:
    d[book['author_id']].append({'title': book['title']})

[{'author_id': k, 'books': v} for k, v in d.items()] # [{'author_id': 'author1', 'books': [{'title': 'book1'}, {'title': 'book3'}]}, {'author_id': 'author2', 'books': [{'title': 'book2'}]}]

这将导致 O(n) 时间复杂度,因为它不需要排序。

使用itertools.groupby,您可以:

key = lambda d: d['author_id']

authors = [
    {'author_id': k, 'books': [{'title': d['title']} for d in g]}
    for k, g in groupby(sorted(books, key=key), key=key)
]

这将按 author_id (k) 对书籍字典进行排序和分组,并为每个组 (g) 累积书名。

顺便说一句,下面的结构会不会更简单而不丢失信息:

authors = {
    k: [d['title'] for d in g]
    for k, g in groupby(sorted(books, key=key), key=key)
}

# {
#     'author1': ['book1', 'book3'],
#     'author2': ['book2']
# }

这对我有用,只需在字典中收集作者,最后 return 构造列表:

def trans(books):
    authors = {}
    for bk in books:
        if bk['author_id'] not in authors:
            authors[bk['author_id']] = [{'title': bk['title']}]
        else:
            authors[bk['author_id']].append({'title': bk['title']})

    return [{'author_id': k, 'books': authors[k]} for k in authors]

这对我有用。没有多个循环使用 map

authors_map = {}
authors = []
for index, book in enumerate(books):
    if book['author_id'] in authors_map:
        authors[authors_map[book['author_id']]][
            'books'].append({'title': book['title']})
    else:
        authors_map[book['author_id']] = len(authors)
        authors.append({'author_id': book['author_id'], 'books': [
                       {'title': book['title']}]})