Saya punya dua daftar, yang pertama dijamin berisi tepat satu item lebih banyak daripada yang kedua . Saya ingin tahu cara paling Pythonic untuk membuat daftar baru yang nilai indeks genapnya berasal dari daftar pertama dan yang nilai indeks ganjilnya berasal dari daftar kedua.
# example inputs
list1 = ['f', 'o', 'o']
list2 = ['hello', 'world']
# desired output
['f', 'hello', 'o', 'world', 'o']
Ini berfungsi, tetapi tidak cantik:
list3 = []
while True:
try:
list3.append(list1.pop(0))
list3.append(list2.pop(0))
except IndexError:
break
Bagaimana lagi ini bisa dicapai? Apa pendekatan yang paling Pythonic?
Jawaban:
Inilah salah satu cara melakukannya dengan mengiris:
>>> list1 = ['f', 'o', 'o'] >>> list2 = ['hello', 'world'] >>> result = [None]*(len(list1)+len(list2)) >>> result[::2] = list1 >>> result[1::2] = list2 >>> result ['f', 'hello', 'o', 'world', 'o']
sumber
Ada resep untuk ini di
itertools
dokumentasi :from itertools import cycle, islice def roundrobin(*iterables): "roundrobin('ABC', 'D', 'EF') --> A D E B F C" # Recipe credited to George Sakkis pending = len(iterables) nexts = cycle(iter(it).next for it in iterables) while pending: try: for next in nexts: yield next() except StopIteration: pending -= 1 nexts = cycle(islice(nexts, pending))
EDIT:
Untuk versi python lebih dari 3:
from itertools import cycle, islice def roundrobin(*iterables): "roundrobin('ABC', 'D', 'EF') --> A D E B F C" # Recipe credited to George Sakkis pending = len(iterables) nexts = cycle(iter(it).__next__ for it in iterables) while pending: try: for next in nexts: yield next() except StopIteration: pending -= 1 nexts = cycle(islice(nexts, pending))
sumber
zip_longest
.itertools
dokumentasi karena.next()
tidak berfungsi lagi.__next__
. Itu tidak tertulis dalam dokumentasi jadi saya mengusulkan edit untuk jawabannya.Ini harus melakukan apa yang Anda inginkan:
>>> iters = [iter(list1), iter(list2)] >>> print list(it.next() for it in itertools.cycle(iters)) ['f', 'hello', 'o', 'world', 'o']
sumber
roundrobin
fungsinya agak berlebihan untuk situasi ini.list(itertools.chain(map(next, itertools.cycle(iters)), *iters))
import itertools print [x for x in itertools.chain.from_iterable(itertools.izip_longest(list1,list2)) if x]
Saya pikir ini adalah cara paling pythonic untuk melakukannya.
sumber
None
s yang * seharusnya ada dalam daftar. Saya akan mengedit dalam versi tweak untuk memperbaikinyaFalse
, atau bahkan hal-hal yang hanya akan dievaluasi sebagaiFalse
olehif
-expression, seperti misalnya0
atau daftar kosong. Hal ini dapat (sebagian) dihindari dengan berikut:[x for x in itertools.chain.from_iterable(itertools.zip_longest(list1, list2)) if x is not None]
. Tentu saja, ini tetap tidak akan berhasil jika daftar berisiNone
elemen yang perlu dipertahankan. Dalam hal ini, Anda perlu mengubahfillvalue
argumenzip_longest
, seperti yang sudah disarankan Dubslow.None
Masalah tampaknya hilang, setidaknya sejak Python 3.7.6 (saya tidak tahu untuk versi yang lebih lama). Jikaalt_chain
didefinisikan sebagaidef alt_chain(*iters, fillvalue=None): return chain.from_iterable(zip_longest(*iters, fillvalue=fillvalue))
, makalist(alt_chain([0, False, 1, set(), 3, 4], [0, None, 1, {}], fillvalue=99))
mengembalikan dengan benar[0, 0, False, None, 1, 1, set(), {}, 3, 99, 4, 99]
.Tanpa itertools dan mengasumsikan l1 adalah 1 item lebih panjang dari l2:
>>> sum(zip(l1, l2+[0]), ())[:-1] ('f', 'hello', 'o', 'world', 'o')
Menggunakan itertools dan berasumsi bahwa daftar tidak berisi None:
>>> filter(None, sum(itertools.izip_longest(l1, l2), ())) ('f', 'hello', 'o', 'world', 'o')
sumber
[(l1[0], l2[0]), (l1[1], l2[1]), ...]
,.sum
menggabungkan tupel bersama:(l1[0], l2[0]) + (l1[1], l2[1]) + ...
menghasilkan daftar berselang-seling. Sisa satu baris hanyalah padding dari l1 dengan elemen tambahan agar zip berfungsi dan potong hingga -1 untuk menghilangkan padding itu.filter(None, ...
(bisa digunakanbool
sebagai gantinya, atauNone.__ne__
) menghapus nilai yang salah, termasuk 0, None dan string kosong, jadi ekspresi kedua tidak sepenuhnya sama dengan yang pertama.sum
melakukannya? Apa peran argumen kedua di sana? Dalam dokumentasinya, argumen kedua adalahstart
.Saya tahu pertanyaan menanyakan tentang dua daftar dengan satu memiliki satu item lebih banyak dari yang lain, tetapi saya pikir saya akan menanyakan ini untuk orang lain yang mungkin menemukan pertanyaan ini.
Berikut adalah solusi Duncan yang disesuaikan untuk bekerja dengan dua daftar ukuran berbeda.
list1 = ['f', 'o', 'o', 'b', 'a', 'r'] list2 = ['hello', 'world'] num = min(len(list1), len(list2)) result = [None]*(num*2) result[::2] = list1[:num] result[1::2] = list2[:num] result.extend(list1[num:]) result.extend(list2[num:]) result
Output ini:
['f', 'hello', 'o', 'world', 'o', 'b', 'a', 'r']
sumber
Jika kedua daftar memiliki panjang yang sama, Anda dapat melakukan:
[x for y in zip(list1, list2) for x in y]
Karena daftar pertama memiliki satu elemen lagi, Anda dapat menambahkannya post hoc:
[x for y in zip(list1, list2) for x in y] + [list1[-1]]
sumber
Inilah satu liner yang melakukannya:
list3 = [ item for pair in zip(list1, list2 + [0]) for item in pair][:-1]
sumber
def combine(list1, list2): lst = [] len1 = len(list1) len2 = len(list2) for index in range( max(len1, len2) ): if index+1 <= len1: lst += [list1[index]] if index+1 <= len2: lst += [list2[index]] return lst
sumber
def combine(list1, list2, lst=[]):
, itulah komentar saya. Namun, pada saat saya mengirimkan komentar itu, killown telah membuat perubahan yang diperlukan.Yang ini didasarkan pada kontribusi Carlos Valiente di atas dengan opsi untuk mengganti kelompok yang terdiri dari banyak item dan memastikan bahwa semua item ada dalam keluaran:
A=["a","b","c","d"] B=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] def cyclemix(xs, ys, n=1): for p in range(0,int((len(ys)+len(xs))/n)): for g in range(0,min(len(ys),n)): yield ys[0] ys.append(ys.pop(0)) for g in range(0,min(len(xs),n)): yield xs[0] xs.append(xs.pop(0)) print [x for x in cyclemix(A, B, 3)]
Ini akan menghubungkan daftar A dan B dengan kelompok dari 3 nilai masing-masing:
['a', 'b', 'c', 1, 2, 3, 'd', 'a', 'b', 4, 5, 6, 'c', 'd', 'a', 7, 8, 9, 'b', 'c', 'd', 10, 11, 12, 'a', 'b', 'c', 13, 14, 15]
sumber
Mungkin agak terlambat membeli satu lagi python one-liner. Ini berfungsi jika kedua daftar memiliki ukuran yang sama atau tidak sama. Satu hal yang tidak berharga adalah ia akan memodifikasi a dan b. Jika ini merupakan masalah, Anda perlu menggunakan solusi lain.
a = ['f', 'o', 'o'] b = ['hello', 'world'] sum([[a.pop(0), b.pop(0)] for i in range(min(len(a), len(b)))],[])+a+b ['f', 'hello', 'o', 'world', 'o']
sumber
Saya mengambil:
a = "hlowrd" b = "el ol" def func(xs, ys): ys = iter(ys) for x in xs: yield x yield ys.next() print [x for x in func(a, b)]
sumber
Berikut satu liner yang menggunakan pemahaman daftar, tanpa pustaka lain:
list3 = [sub[i] for i in range(len(list2)) for sub in [list1, list2]] + [list1[-1]]
Berikut adalah pendekatan lain, jika Anda mengizinkan perubahan list1 awal Anda dengan efek samping:
[list1.insert((i+1)*2-1, list2[i]) for i in range(len(list2))]
sumber
from itertools import chain list(chain(*zip('abc', 'def'))) # Note: this only works for lists of equal length ['a', 'd', 'b', 'e', 'c', 'f']
sumber
Berhenti paling cepat:
def interlace(*iters, next = next) -> collections.Iterable: """ interlace(i1, i2, ..., in) -> ( i1-0, i2-0, ..., in-0, i1-1, i2-1, ..., in-1, . . . i1-n, i2-n, ..., in-n, ) """ return map(next, cycle([iter(x) for x in iters]))
Tentu, menyelesaikan metode berikutnya / __ next__ mungkin lebih cepat.
sumber
Ini buruk tetapi berfungsi tidak peduli ukuran daftarnya:
list3 = [element for element in list(itertools.chain.from_iterable([val for val in itertools.izip_longest(list1, list2)])) if element != None]
sumber
Beberapa kalimat satu baris terinspirasi oleh jawaban untuk pertanyaan lain :
import itertools list(itertools.chain.from_iterable(itertools.izip_longest(list1, list2, fillvalue=object)))[:-1] [i for l in itertools.izip_longest(list1, list2, fillvalue=object) for i in l if i is not object] [item for sublist in map(None, list1, list2) for item in sublist][:-1]
sumber
Bagaimana dengan numpy? Ia bekerja dengan string juga:
import numpy as np np.array([[a,b] for a,b in zip([1,2,3],[2,3,4,5,6])]).ravel()
Hasil:
array([1, 2, 2, 3, 3, 4])
sumber
Alternatif dengan cara yang fungsional & tidak berubah (Python 3):
from itertools import zip_longest from functools import reduce reduce(lambda lst, zipped: [*lst, *zipped] if zipped[1] != None else [*lst, zipped[0]], zip_longest(list1, list2),[])
sumber
Saya akan melakukan yang sederhana:
Ini akan muncul dengan iterator tanpa membuat kebutuhan penyimpanan tambahan.
sumber
chain.from_iterable(izip(list1, list2), list1[len(list2):])
untuk masalah khusus yang ditanyakan di sini ... list1 seharusnya menjadi yang lebih panjang.Saya terlalu tua untuk memahami daftar, jadi:
import operator list3 = reduce(operator.add, zip(list1, list2))
sumber