Скопируйте вложенный список и заполните его последовательными номерами

Учитывая этот вложенный список:

foo = [["apple", "cherry"], ["banana"], ["pear", "raspberry", "pineapple"]]

Я хочу сохранить структуру и заменить все элементы последовательными номерами. Мой желаемый результат:

[[0, 1], [2], [3, 4, 5]]

Я надеялся на простой однострочник, но самое короткое рабочее решение, которое я придумал, было:

foo_numbers = []
count = 0

for i, sublist in enumerate(foo):
    foo_numbers.append([])
    for item in sublist:
        foo_numbers[i].append(count)
        count += 1

Обычно эти ручные итераторы указывают на то, что есть более питонический способ добиться того же. Если это должно быть сделано с пониманием списка, я не мог понять, как создать «общий счетчик» для обоих циклов, чтобы он не начинался с нуля для каждого sublist.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
75
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вы можете использовать itertools.count с пониманием вложенного списка:

from itertools import count

foo = [["apple", "cherry"], ["banana"], ["pear", "raspberry", "pineapple"]]

c = count()  # 0 start is default, e.g. count(1) will start from 1
res = [[next(c) for _ in lst] for lst in foo]

print(res)
# [[0, 1], [2], [3, 4, 5]]

Это аккуратно! По умолчанию я не импортирую itertools и обычно предпочитаю простой python, но это определенно лучше моего громоздкого решения.

offeltoffel 23.01.2019 14:16

Просто обратите внимание: использование timeit, кажется, указывает на то, что это решение медленнее, чем ваш исходный код.

Ralf 23.01.2019 14:17

Сравнивая эти решения:

import itertools

def f1(foo):
    """original version in question"""
    foo_numbers = []
    count = 0
    for i, sublist in enumerate(foo):
        foo_numbers.append([])
        for item in sublist:
            foo_numbers[i].append(count)
            count += 1
    return foo_numbers

def f2(foo):
    """@jpp answer"""
    c = itertools.count()
    return [[next(c) for _ in range(len(lst))] for lst in foo]

def f3(foo):
    """@DanielMesejo answer"""
    foo_numbers = []
    count = 0
    for i, sublist in enumerate(foo):
        foo_numbers.append([j for j in range(count, len(sublist) + count)])
        count += len(sublist)
    return foo_numbers

Дает нам это:

>>> import timeit
>>>
>>> timeit.timeit('f(foo)', 'from __main__ import f1 as f, foo')
1.2990377170499414
>>> timeit.timeit('f(foo)', 'from __main__ import f2 as f, foo')
2.260929798008874
>>> timeit.timeit('f(foo)', 'from __main__ import f3 as f, foo')
2.1552230638917536

Кажется, оригинальная версия быстрее (в 2 раза).

Спасибо за указание на это! На самом деле время не имеет значения. Мой список на самом деле не намного больше, чем код примера, и он вызывается только один раз. Но эти циклы for оставляют меня в недоумении, когда я возвращаюсь к коду год спустя и задаюсь вопросом, что они делают. Поэтому я искал что-то тонкое

offeltoffel 23.01.2019 14:42

Почему два вложенных for loop быстрее, чем list comprehension?

offeltoffel 23.01.2019 14:52

@offeltoffel Я тоже не знаю, почему в этом случае циклы for работают быстрее, чем понимание списка. Я только что сделал временные тесты и сам был удивлен.

Ralf 23.01.2019 15:01

Вы можете использовать понимание списка вместо внутреннего цикла:

foo = [["apple", "cherry"], ["banana"], ["pear", "raspberry", "pineapple"]]

foo_numbers = []
count = 0

for i, sublist in enumerate(foo):
    foo_numbers.append([j for j in range(count, len(sublist) + count)])
    count += len(sublist)

print(foo_numbers)

Вывод

[[0, 1], [2], [3, 4, 5]]

В общем, понимание списков быстрее, чем циклы для создания списков. В качестве альтернативы вы можете преобразовать объект диапазона в список, например:

foo_numbers.append(list(range(count, len(sublist) + count)))
range(count, len(sublist) + count) был своего рода отсутствующим кусочком головоломки, которого не хватало, чтобы составить это решение самостоятельно. Спасибо за прояснение моего зрения там
offeltoffel 23.01.2019 14:44

Просто примечание: см. мой ответ; кажется, что эта версия также медленнее исходного кода

Ralf 23.01.2019 14:50

Используйте приведенное ниже решение, если вы действительно не надо хотите что-либо импортировать:

foo = [["apple", "cherry"], ["banana"], ["pear", "raspberry", "pineapple"]]

count = -1

def indexer(fruits):
    global count
    count += 1
    return count


foo_new = list(map(lambda fruits: list(map(indexer, fruits)), foo))

print(foo_new)              

Выходы:

[[0, 1], [2], [3, 4, 5]]

В этом ответе есть несколько хороших идей. Я на самом деле не ограничен, это скорее личные предпочтения. Спасибо!

offeltoffel 23.01.2019 14:43

Другие вопросы по теме