Iterasi setiap dua elemen dalam daftar

209

Bagaimana cara membuat forloop atau daftar pemahaman sehingga setiap iterasi memberi saya dua elemen?

l = [1,2,3,4,5,6]

for i,k in ???:
    print str(i), '+', str(k), '=', str(i+k)

Keluaran:

1+2=3
3+4=7
5+6=11
jackhab
sumber
2
Untuk pasangan yang tumpang tindih: stackoverflow.com/questions/5434891/…
user202729

Jawaban:

231

Anda memerlukan pairwise()(atau grouped()) implementasi.

Untuk Python 2:

from itertools import izip

def pairwise(iterable):
    "s -> (s0, s1), (s2, s3), (s4, s5), ..."
    a = iter(iterable)
    return izip(a, a)

for x, y in pairwise(l):
   print "%d + %d = %d" % (x, y, x + y)

Atau, lebih umum:

from itertools import izip

def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return izip(*[iter(iterable)]*n)

for x, y in grouped(l, 2):
   print "%d + %d = %d" % (x, y, x + y)

Di Python 3, Anda bisa mengganti izipdengan zip()fungsi bawaan, dan lepaskanimport .

Semua kredit untuk martineau untuk jawaban atas pertanyaan saya , saya telah menemukan ini sangat efisien karena hanya mengulangi sekali dalam daftar dan tidak membuat daftar yang tidak perlu dalam proses.

NB : Ini tidak harus bingung dengan pairwiseresep dalam itertoolsdokumentasi Python sendiri , yang menghasilkan s -> (s0, s1), (s1, s2), (s2, s3), ..., seperti yang ditunjukkan oleh @ lazyr dalam komentar.

Tambahan kecil untuk mereka yang ingin melakukan pengecekan ketik dengan mypy pada Python 3:

from typing import Iterable, Tuple, TypeVar

T = TypeVar("T")

def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
    """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
    return zip(*[iter(iterable)] * n)
Johnsyweb
sumber
16
Jangan bingung dengan fungsi berpasangan yang disarankan di bagian resep itertools , yang menghasilkans -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow
1
Itu melakukan hal yang berbeda. Versi Anda hanya menghasilkan setengah jumlah pasangan dibandingkan dengan itertoolsfungsi resep dengan nama yang sama. Tentu saja milikmu lebih cepat ...
Sven Marnach
Hah? Fungsi Anda dan fungsi yang saya maksudkan untuk melakukan hal-hal yang berbeda, dan itulah inti dari komentar saya.
Lauritz V. Thaulow
5
HATI-HATI! Menggunakan fungsi-fungsi ini menempatkan Anda pada risiko tidak mengulangi elemen-elemen terakhir dari iterable. Contoh: daftar (dikelompokkan ([1,2,3], 2)) >>> [(1, 2)] .. saat Anda mengharapkan [(1,2), (3,)]
egafni
4
@ Erik49: Dalam kasus yang ditentukan dalam pertanyaan, tidak masuk akal untuk memiliki tuple 'tidak lengkap'. Jika Anda ingin menyertakan tupel tidak lengkap, Anda bisa menggunakan izip_longest()bukan izip(). Misalnya: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. Semoga ini membantu.
Johnsyweb
191

Anda perlu dua elemen, jadi

data = [1,2,3,4,5,6]
for i,k in zip(data[0::2], data[1::2]):
    print str(i), '+', str(k), '=', str(i+k)

Dimana:

  • data[0::2] artinya membuat subset kumpulan elemen itu (index % 2 == 0)
  • zip(x,y) membuat koleksi tuple dari koleksi x dan y elemen indeks yang sama.
Margus
sumber
8
Ini juga dapat diperpanjang jika diperlukan lebih dari dua elemen. Misalnyafor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
lifebalance
19
Jauh lebih bersih daripada menarik impor dan mendefinisikan fungsi!
kmarsh
7
@kmarsh: Tapi ini hanya bekerja pada urutan, fungsinya bekerja pada semua iterable; dan ini menggunakan ruang ekstra O (N), fungsinya tidak; di sisi lain, ini biasanya lebih cepat. Ada alasan bagus untuk memilih satu atau yang lain; takut importbukan salah satu dari mereka.
abarnert
77
>>> l = [1,2,3,4,5,6]

>>> zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

>>> zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]

>>> [a+b for a,b in zip(l,l[1:])[::2]]
[3, 7, 11]

>>> ["%d + %d = %d" % (a,b,a+b) for a,b in zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']
pyanon
sumber
1
Ini tidak bekerja pada Python-3.6.0 tetapi masih bekerja pada Python-2.7.10
Hamid Rohani
6
@HamidRohani zipmengembalikan zipobjek dengan Python 3, yang tidak dapat disubkripsikan . Itu perlu dikonversi ke urutan ( list,, tupledll) terlebih dahulu, tetapi "tidak bekerja" adalah sedikit peregangan.
vaultah
58

Solusi sederhana.

l = [1, 2, 3, 4, 5, 6]

untuk saya dalam kisaran (0, len (l), 2):
    mencetak str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])
taskinoor
sumber
1
bagaimana jika daftar Anda tidak genap, dan Anda ingin menunjukkan nomor terakhir seperti apa adanya?
Hans de Jong
@HansdeJong tidak mengerti Anda. Tolong jelaskan sedikit lebih banyak.
taskinoor
2
Terima kasih. Saya sudah tahu bagaimana melakukannya. Masalahnya adalah jika Anda memiliki daftar yang bahkan tidak memiliki jumlah angka di dalamnya, itu akan mendapatkan kesalahan indeks. Mengatasinya dengan mencoba: kecuali:
Hans de Jong
Atau ((l[i], l[i+1])for i in range(0, len(l), 2))untuk generator, dapat dengan mudah dimodifikasi untuk tupel yang lebih panjang.
Basel Shishani
44

Sementara semua jawaban yang digunakan zipsudah benar, saya menemukan bahwa mengimplementasikan sendiri fungsionalitas itu mengarah ke kode yang lebih mudah dibaca:

def pairwise(it):
    it = iter(it)
    while True:
        try:
            yield next(it), next(it)
        except StopIteration:
            # no more elements in the iterator
            return

Bagian ini it = iter(it)memastikan bahwa itsebenarnya merupakan iterator, bukan hanya iterable. Jika itsudah merupakan iterator, baris ini adalah no-op.

Pemakaian:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)
mic_e
sumber
2
Solusi ini memungkinkan generalisasi untuk ukuran tupel> 2
guilloptero
1
Solusi ini juga berfungsi jika ithanya merupakan iterator dan bukan iterable. Solusi lain tampaknya bergantung pada kemungkinan untuk membuat dua iterator independen untuk urutan.
meroket
Saya menemukan pendekatan ini di stackoverflow.com/a/16815056/2480481 sebelum melihat jawaban ini. Lebih bersih, lebih mudah daripada berurusan dengan zip ().
m3nda
2
Saya suka itu memungkinkan untuk menghindari penggunaan memori tiga kali lipat sebagai jawaban yang diterima.
Kentzo
Ini tidak berfungsi dengan baik dengan forloop di Python 3.5+ karena PEP 479 , yang menggantikan setiap StopIterationpeningkatan dalam generator dengan a RuntimeError.
sidney
28

Saya harap ini akan menjadi cara yang lebih elegan untuk melakukannya.

a = [1,2,3,4,5,6]
zip(a[::2], a[1::2])

[(1, 2), (3, 4), (5, 6)]
Vivek Srinivasan
sumber
18

Jika Anda tertarik dengan kinerja, saya melakukan tolok ukur kecil (menggunakan perpustakaan saya simple_benchmark) untuk membandingkan kinerja solusi dan saya menyertakan fungsi dari salah satu paket saya:iteration_utilities.grouper

from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder

bench = BenchmarkBuilder()

@bench.add_function()
def Johnsyweb(l):
    def pairwise(iterable):
        "s -> (s0, s1), (s2, s3), (s4, s5), ..."
        a = iter(iterable)
        return zip(a, a)

    for x, y in pairwise(l):
        pass

@bench.add_function()
def Margus(data):
    for i, k in zip(data[0::2], data[1::2]):
        pass

@bench.add_function()
def pyanon(l):
    list(zip(l,l[1:]))[::2]

@bench.add_function()
def taskinoor(l):
    for i in range(0, len(l), 2):
        l[i], l[i+1]

@bench.add_function()
def mic_e(it):
    def pairwise(it):
        it = iter(it)
        while True:
            try:
                yield next(it), next(it)
            except StopIteration:
                return

    for a, b in pairwise(it):
        pass

@bench.add_function()
def MSeifert(it):
    for item1, item2 in grouper(it, 2):
        pass

bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)

masukkan deskripsi gambar di sini

Jadi, jika Anda menginginkan solusi tercepat tanpa dependensi eksternal, Anda mungkin harus menggunakan pendekatan yang diberikan oleh Johnysweb (pada saat penulisan ini adalah jawaban yang paling banyak dipilih dan diterima).

Jika Anda tidak keberatan dengan ketergantungan tambahan maka grouperdari iteration_utilitiesmungkin akan sedikit lebih cepat.

Pikiran tambahan

Beberapa pendekatan memiliki beberapa batasan, yang belum dibahas di sini.

Misalnya beberapa solusi hanya bekerja untuk urutan (yaitu daftar, string, dll.), Misalnya solusi Margus / pyanon / taskinoor yang menggunakan pengindeksan sementara solusi lain bekerja pada setiap iterable (yaitu urutan dan generator, iterator) seperti Johnysweb / mic_e / solusi saya.

Kemudian Johnysweb juga memberikan solusi yang berfungsi untuk ukuran selain 2 sedangkan jawaban lainnya tidak (oke, the iteration_utilities.grouper juga memungkinkan pengaturan jumlah elemen ke "grup").

Lalu ada juga pertanyaan tentang apa yang harus terjadi jika ada jumlah elemen ganjil dalam daftar. Haruskah barang yang tersisa dibubarkan? Haruskah daftar itu diisi dengan empuk agar ukurannya rata? Haruskah barang yang tersisa dikembalikan sebagai tunggal? Jawaban lainnya tidak membahas hal ini secara langsung, namun jika saya tidak mengabaikan apa pun mereka semua mengikuti pendekatan bahwa item yang tersisa harus diberhentikan (kecuali untuk jawaban taskinoors - yang benar-benar akan meningkatkan Pengecualian).

Dengan grouperAnda dapat memutuskan apa yang ingin Anda lakukan:

>>> from iteration_utilities import grouper

>>> list(grouper([1, 2, 3], 2))  # as single
[(1, 2), (3,)]

>>> list(grouper([1, 2, 3], 2, truncate=True))  # ignored
[(1, 2)]

>>> list(grouper([1, 2, 3], 2, fillvalue=None))  # padded
[(1, 2), (3, None)]
MSeifert
sumber
12

Gunakan zipdan iterperintah bersama:

Saya merasa solusi ini itercukup elegan:

it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]

Yang saya temukan di dokumentasi zip Python 3 .

it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='\n')

# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11

Untuk menggeneralisasi ke Nelemen sekaligus:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]
Mekanika Kuantum
sumber
10
for (i, k) in zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

zip(*iterable) mengembalikan tuple dengan elemen berikutnya dari setiap iterable.

l[::2] mengembalikan elemen ke-1, ke-3, ke-5, dll. dari daftar: tanda titik dua menunjukkan bahwa irisan dimulai di awal karena tidak ada angka di belakangnya, tanda titik dua hanya diperlukan jika Anda menginginkan langkah 'pada irisan '(dalam hal ini 2).

l[1::2]melakukan hal yang sama tetapi dimulai pada elemen kedua daftar sehingga mengembalikan elemen ke-2, ke-4, ke-6, dll. dari daftar asli .

alexpinho98
sumber
4
Jawaban ini sudah diberikan oleh Margus dua tahun lalu. stackoverflow.com/questions/5389507/…
cababunga
1
1 untuk menjelaskan cara [number::number]kerja sintaksis. sangat membantu bagi mereka yang tidak sering menggunakan python
Alby
2

Dengan membongkar:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(str(i), '+', str(k), '=', str(i+k))
toaruScar
sumber
Wow! Mengapa saya tidak bisa memikirkan hal ini :) Anda hanya perlu menangani kasus di mana tidak ada pasangan absolut (entri ganjil)
Saurav Kumar
1

Bagi siapa pun yang mungkin bisa membantu, berikut adalah solusi untuk masalah yang serupa tetapi dengan pasangan yang tumpang tindih (bukan pasangan yang saling eksklusif).

Dari dokumentasi Python itertools :

from itertools import izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

Atau, lebih umum:

from itertools import izip

def groupwise(iterable, n=2):
    "s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
    t = tee(iterable, n)
    for i in range(1, n):
        for j in range(0, i):
            next(t[i], None)
    return izip(*t)
Chris Malek
sumber
1

Anda dapat menggunakan paket more_itertools .

import more_itertools

lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
    print(f'{i} + {j} = {i+j}')
Scott Ming
sumber
1

Saya perlu membagi daftar dengan nomor dan memperbaikinya seperti ini.

l = [1,2,3,4,5,6]

def divideByN(data, n):
        return [data[i*n : (i+1)*n] for i in range(len(data)//n)]  

>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]

>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]
Ali Katkar
sumber
1

Ada banyak cara untuk melakukan itu. Sebagai contoh:

lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]    
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

[i for i in zip(*[iter(lst)]*2)]    
>>>[(1, 2), (3, 4), (5, 6)]
Binit Bhagat
sumber
0

Berpikir bahwa ini adalah tempat yang baik untuk membagikan generalisasi saya tentang ini untuk n> 2, yang hanya merupakan jendela geser di atas iterable:

def sliding_window(iterable, n):
    its = [ itertools.islice(iter, i, None) 
            for i, iter
            in enumerate(itertools.tee(iterable, n)) ]                               

    return itertools.izip(*its)
Yuval
sumber
0

Judul pertanyaan ini menyesatkan, Anda tampaknya mencari pasangan yang berurutan, tetapi jika Anda ingin mengulangi semua pasangan yang memungkinkan, maka ini akan berhasil:

for i,v in enumerate(items[:-1]):
        for u in items[i+1:]:
Ofek Ron
sumber
0

Menggunakan pengetikan sehingga Anda dapat memverifikasi data menggunakan alat analisis statis mypy :

from typing import Iterator, Any, Iterable, TypeVar, Tuple

T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]

def legs(iterable: Iterator[T_]) -> Pairs_Iter:
    begin = next(iterable)
    for end in iterable:
        yield begin, end
        begin = end
Vlad Bezden
sumber
0

Pendekatan sederhana:

[(a[i],a[i+1]) for i in range(0,len(a),2)]

ini berguna jika array Anda adalah dan Anda ingin mengulanginya secara berpasangan. Untuk beralih pada kembar tiga atau lebih, cukup ubah perintah langkah "rentang", misalnya:

[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]

(Anda harus berurusan dengan nilai berlebih jika panjang dan langkah array Anda tidak cocok)

mchrgr2000
sumber
-1

Di sini kita dapat memiliki alt_elemmetode yang sesuai untuk loop Anda.

def alt_elem(list, index=2):
    for i, elem in enumerate(list, start=1):
        if not i % index:
           yield tuple(list[i-index:i])


a = range(10)
for index in [2, 3, 4]:
    print("With index: {0}".format(index))
    for i in alt_elem(a, index):
       print(i)

Keluaran:

With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)

Catatan: Solusi di atas mungkin tidak efisien mengingat operasi yang dilakukan di func.

Sanket Sudake
sumber
-1
a_list = [1,2,3,4,5,6]
empty_list = [] 
for i in range(0,len(a_list),2):
   empty_list.append(a_list[i]+a_list[i+1])   
print(empty_list)
pengguna391
sumber