前の値と次の値にもアクセスするPythonループ


91

オブジェクトのリストを繰り返し処理して、前、現在、次のアイテムにアクセスするにはどうすればよいですか?このC / C ++コードのように、Pythonで?


fooがリストの最初または最後にある場合はどうなりますか?現在、これは配列の範囲外になります。
ブライアン

2
「foo」の最初の出現が必要な場合は、一致したときに「for」ブロックから「break」を実行します。
van

1番目(0番目ではない)の要素で反復を開始し、最後の1つだけの要素で反復を終了しますか?
SMCI

fooリスト内で1回だけ発生することが保証されていますか?それが複数発生した場合、ここでのいくつかのアプローチは失敗するか、最初のものだけを見つけます。また、発生しない場合は、他のアプローチが失敗するか、ValueErrorなどの例外がスローされます。いくつかのテストケースを与えることは助けになったでしょう。
SMCI

また、ここでの例は、既知の長さを持ち、インデックス付け可能なオブジェクトのシーケンスです。ここでの回答のいくつかは、イテレータに一般化されています。イテレータは、常にインデックス可能であるとは限らず、常に長さがあるとは限らず、常に有限であるとは限りません。
smci20年

回答:


110

これでうまくいくはずです。

foo = somevalue
previous = next_ = None
l = len(objects)
for index, obj in enumerate(objects):
    if obj == foo:
        if index > 0:
            previous = objects[index - 1]
        if index < (l - 1):
            next_ = objects[index + 1]

これがenumerate関数に関するドキュメントです。


18
ただし、「next」は組み込み関数であるため、変数名として使用しないことをお勧めします。
mkosmala 2015

1
ループの終わりに:この編集されたバージョンは、論理的に音はまだないobjnext_意図しない副作用を有することができる最後の反復のために同じオブジェクトになります。
TemporalWolf

質問ステートメントは、OPが1番目(0番目ではない)の要素で反復を開始し、最後の1つの要素で反復を終了することを明示的に示しています。したがって、ここにindexあるようにで1 ... (l-1)はなく0 ... l、から実行する必要があり、特別な場合のif句は必要ありません。ところで、パラメータはありますenumerate(..., start=1)が、ではありませんend。したがって、実際には使用したくありませんenumerate()
SMCI

147

これまでのソリューションはリストのみを扱い、ほとんどがリストをコピーしています。私の経験では、それは不可能なことがよくあります。

また、リストに要素を繰り返すことができるという事実も扱っていません。

質問のタイトルには「ループ内の前の値と次の値書かれていますが、ここでほとんどの回答をループ内で実行すると、各要素でリスト全体を繰り返して検索することになります。

だから私はちょうどその関数を作成しました。itertoolsモジュールを使用して、反復可能オブジェクトを分割およびスライスし、前の要素と次の要素を一緒にタプルを生成します。コードの機能は正確ではありませんが、問題を解決できる可能性があるため、一見の価値があります。

from itertools import tee, islice, chain, izip

def previous_and_next(some_iterable):
    prevs, items, nexts = tee(some_iterable, 3)
    prevs = chain([None], prevs)
    nexts = chain(islice(nexts, 1, None), [None])
    return izip(prevs, items, nexts)

次に、それをループで使用すると、前と次のアイテムが含まれます。

mylist = ['banana', 'orange', 'apple', 'kiwi', 'tomato']

for previous, item, nxt in previous_and_next(mylist):
    print "Item is now", item, "next is", nxt, "previous is", previous

結果:

Item is now banana next is orange previous is None
Item is now orange next is apple previous is banana
Item is now apple next is kiwi previous is orange
Item is now kiwi next is tomato previous is apple
Item is now tomato next is None previous is kiwi

これは、任意のサイズのリスト(リストをコピーしないため)、および任意の反復可能(ファイル、セットなど)で機能します。このようにして、シーケンスを繰り返すだけで、ループ内で前のアイテムと次のアイテムを使用できるようになります。シーケンス内のアイテムを再度検索する必要はありません。

コードの簡単な説明:

  • tee 入力シーケンス上で3つの独立したイテレータを効率的に作成するために使用されます
  • chain2つのシーケンスを1つにリンクします。ここでは、単一要素のシーケンス[None]をに追加するために使用されますprevs
  • islice最初を除くすべての要素のシーケンスを作成するために使用され、次にその末尾にchainを追加するNoneために使用されます
  • これに基づいてsome_iterable、次のような3つの独立したシーケンスがあります。
    • prevsNone, A, B, C, D, E
    • itemsA, B, C, D, E
    • nextsB, C, D, E, None
  • 最後に、izip3つのシーケンスを1つのトリプレットシーケンスに変更するために使用されます。

izip入力シーケンスが使い果たされると停止することに注意してください。そのため、の最後の要素はprevs無視されます。これは正しいprevです。最後の要素がその要素になるような要素はありません。私たちは、から最後の要素を取り除くしようとすることができprevsますがizipの行動は、その冗長になります

また、それに注意してくださいteeizipisliceおよびchainから来るitertoolsモジュール。それらは入力シーケンスをオンザフライで(怠惰に)操作するため、効率的になり、シーケンス全体を一度にメモリに保持する必要がなくなります。

ではpython 3、インポート中にエラーが表示されます。の代わりにizip使用できます。インポートする必要はありません。- sourceで事前定義されています。zipizipzippython 3


3
@becomingGuru:SOをPythonリファレンスドキュメントのミラーに変える必要はありません。これらのすべての機能は、公式ドキュメントで(例を使用して)非常にうまく説明されています
Eli Bendersky 2009年

1
@becomingGuru:ドキュメントへのリンクを追加しました。
nosklo 2009年

6
@LakshmanPrasad私はウィキ気分なので、いくつかの説明を追加しました:-)。
コス2012

7
Python 3izipでは、組み込みzip関数に置き換えることができることは言及する価値があるかもしれません;-)
Tomasito665 2017

1
これは素晴らしい解決策ですが、複雑すぎるようです。これに触発されたstackoverflow.com/a/54995234/1265955を参照してください。
ビクトリア

6

リスト内包表記を使用して、現在、前、次の要素を持つ3タプルを返します。

three_tuple = [(current, 
                my_list[idx - 1] if idx >= 1 else None, 
                my_list[idx + 1] if idx < len(my_list) - 1 else None) for idx, current in enumerate(my_list)]

4

組み込み関数のみを使用し、他のオフセットに簡単に拡張できるため、これがどのように発生していないのかはわかりません。

values = [1, 2, 3, 4]
offsets = [None] + values[:-1], values, values[1:] + [None]
for value in list(zip(*offsets)):
    print(value) # (previous, current, next)

(None, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, None)

4

境界エラーのないジェネレーターを使用したバージョンは次のとおりです。

def trios(iterable):
    it = iter(iterable)
    try:
        prev, current = next(it), next(it)
    except StopIteration:
        return
    for next in it:
        yield prev, current, next
        prev, current = current, next

def find_prev_next(objects, foo):
    prev, next = 0, 0
    for temp_prev, current, temp_next in trios(objects):
        if current == foo:
            prev, next = temp_prev, temp_next
    return prev, next

print(find_prev_next(range(10), 1))
print(find_prev_next(range(10), 0))
print(find_prev_next(range(10), 10))
print(find_prev_next(range(0), 10))
print(find_prev_next(range(1), 10))
print(find_prev_next(range(2), 10))

コードとは異なり、境界の動作では、最初または最後の要素で「foo」が検索されないことに注意してください。繰り返しますが、境界のセマンティクスは奇妙です...そしてあなたのコードから理解するのは難しいです:)


2

Pythonの簡潔さのための条件式の使用> = 2.5

def prenext(l,v) : 
   i=l.index(v)
   return l[i-1] if i>0 else None,l[i+1] if i<len(l)-1 else None


# example
x=range(10)
prenext(x,3)
>>> (2,4)
prenext(x,0)
>>> (None,2)
prenext(x,9)
>>> (8,None)

2

これに対する解決策を探していて、要素を循環させたい人にとっては、以下がうまくいくかもしれません-

from collections import deque  

foo = ['A', 'B', 'C', 'D']

def prev_and_next(input_list):
    CURRENT = input_list
    PREV = deque(input_list)
    PREV.rotate(-1)
    PREV = list(PREV)
    NEXT = deque(input_list)
    NEXT.rotate(1)
    NEXT = list(NEXT)
    return zip(PREV, CURRENT, NEXT)

for previous_, current_, next_ in prev_and_next(foo):
    print(previous_, current_, next)

最後のnext_でアンダースコア?編集できません-「少なくとも6でなければなりません...」
Xpector19年

単純なforループとアクセスよりもこれが望ましいのはなぜobjects[i-1], objects[i], objects[i+1]ですか?または発電機?それは私には完全に曖昧に思えます。また、PREVとNEXTがデータのコピーを作成するため、不必要に3倍のメモリを使用します。
SMCI

@smcii+1リストの最後の要素に対してどのようにアプローチを機能させるのですか?次の要素が最初になります。範囲外になります。
ElectRocnic

1

ジェネレーターを使用すると、非常に簡単です。

signal = ['→Signal value←']
def pniter( iter, signal=signal ):
    iA = iB = signal
    for iC in iter:
        if iB is signal:
            iB = iC
            continue
        else:
            yield iA, iB, iC
        iA = iB
        iB = iC
    iC = signal
    yield iA, iB, iC

if __name__ == '__main__':
    print('test 1:')
    for a, b, c in pniter( range( 10 )):
        print( a, b, c )
    print('\ntest 2:')
    for a, b, c in pniter([ 20, 30, 40, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 3:')
    cam = { 1: 30, 2: 40, 10: 9, -5: 36 }
    for a, b, c in pniter( cam ):
        print( a, b, c )
    for a, b, c in pniter( cam ):
        print( a, a if a is signal else cam[ a ], b, b if b is signal else cam[ b ], c, c if c is signal else cam[ c ])
    print('\ntest 4:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 5:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ], ['sig']):
        print( a, b, c )
    print('\ntest 6:')
    for a, b, c in pniter([ 20, ['→Signal value←'], None, '→Signal value←', 60, 70, 80 ], signal ):
        print( a, b, c )

シグナル値のチェックでは「is」が使用され、シグナルはPythonがインターンしない値であるため、Noneとシグナル値と同じ値を含むテストは引き続き機能することに注意してください。ただし、任意のシングルトンマーカー値をシグナルとして使用できます。これにより、状況によってはユーザーコードが簡略化される場合があります。


9
「それは非常に簡単だ」
ミゲル・スティーブンス

「センチネル」を意味するときは「シグナル」とは言わないでください。また、if iB is signalsignal = Noneの場合を除いて、オブジェクトが等しいかどうかを比較するために使用することは決してありませんNoneiter組み込みをシャドウするため、引数名として使用しないでくださいiter()。同上next。とにかく発電のアプローチは、単純にすることができyield prev, curr, next_
SMCI

@smciたぶん、あなたの辞書には、シグナルとセンチネルに関して、私のものとは異なる定義があります。同じ値の他のアイテムではなく、特定のアイテムをテストしたかったので、特に「is」を使用しました。「is」はそのテストの正しい演算子です。iterとnextshadowの使用は、他の方法で参照されていないものだけなので、問題はありませんが、同意されており、ベストプラクティスではありません。最後の申し立てのコンテキストを提供するには、さらにコードを表示する必要があります。
ビクトリア

@Victoria: 'sentinel [value]'は明確に定義されたソフトウェア用語であり、 'signal'はそうではありません(信号処理やカーネル信号については話していません)。[とPythonで物事を比較するようis代わりの==]、それはここでは、よく知られた落とし穴だ理由を複数の理由です:あなたはしているが、CPythonのインターンの文字列に頼っているので、あなたは、文字列のためにそれを離れて得ることができますが、その後もv1 = 'monkey'; v2 = 'mon'; v3 = 'key、その後、v1 is (v2 + v3)得られFalse。また、コードがint / stringではなくオブジェクトの使用に切り替わった場合、usingisは機能しなくなります。したがって、一般的には==、平等を比較するために使用する必要があります。
SMCI

@smciコンピュータソフトウェアで最も難しい問題は、さまざまな用語を使用するさまざまなグループの人々が原因で、通信、ネットワークが機能しないことです。ことわざにあるように、標準は素晴らしいです、誰もがそれを持っています。Python ==とis演算子の違いを完全に理解しているので、isを使用することにしました。先入観のある「用語とルール」を見渡すと、==は、同等に比較されるすべてのアイテムがシーケンスを終了できるのに対し、isを使用すると、シグナルとして使用されている特定のオブジェクトでのみ終了することがわかります。 (または必要に応じて歩哨)。
ビクトリア

1

2つの簡単な解決策:

  1. 前の値と次の値の両方の変数を定義する必要がある場合:
alist = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five']

prev = alist[0]
curr = alist[1]

for nxt in alist[2:]:
    print(f'prev: {prev}, curr: {curr}, next: {nxt}')
    prev = curr
    curr = nxt

Output[1]:
prev: Zero, curr: One, next: Two
prev: One, curr: Two, next: Three
prev: Two, curr: Three, next: Four
prev: Three, curr: Four, next: Five
  1. リスト内のすべての値を現在の値変数でトラバースする必要がある場合:
alist = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five']

prev = None
curr = alist[0]

for nxt in alist[1:] + [None]:
    print(f'prev: {prev}, curr: {curr}, next: {nxt}')
    prev = curr
    curr = nxt

Output[2]:
prev: None, curr: Zero, next: One
prev: Zero, curr: One, next: Two
prev: One, curr: Two, next: Three
prev: Two, curr: Three, next: Four
prev: Three, curr: Four, next: Five
prev: Four, curr: Five, next: None

0

indexリストで使用して場所を見つけ、somevalue必要に応じて前と次を取得できます。


def find_prev_next(elem, elements):
    previous, next = None, None
    index = elements.index(elem)
    if index > 0:
        previous = elements[index -1]
    if index < (len(elements)-1):
        next = elements[index +1]
    return previous, next


foo = 'three'
list = ['one','two','three', 'four', 'five']

previous, next = find_prev_next(foo, list)

print previous # should print 'two'
print next # should print 'four'



0

AFAIKこれはかなり速いはずですが、私はそれをテストしませんでした:

def iterate_prv_nxt(my_list):
    prv, cur, nxt = None, iter(my_list), iter(my_list)
    next(nxt, None)

    while True:
        try:
            if prv:
                yield next(prv), next(cur), next(nxt, None)
            else:
                yield None, next(cur), next(nxt, None)
                prv = iter(my_list)
        except StopIteration:
            break

使用例:

>>> my_list = ['a', 'b', 'c']
>>> for prv, cur, nxt in iterate_prv_nxt(my_list):
...    print prv, cur, nxt
... 
None a b
a b c
b c None

0

これはうまくいき、複雑ではないと思います

array= [1,5,6,6,3,2]
for i in range(0,len(array)):
    Current = array[i]
    Next = array[i+1]
    Prev = array[i-1]

Pythonが配列の負のインデックスをサポートしていることを私はほとんど知りませんでした、ありがとう!
ElectRocnic

1
これは、最後に失敗します:(しかし
ElectRocnic

0

非常にC / C ++スタイルのソリューション:

    foo = 5
    objectsList = [3, 6, 5, 9, 10]
    prev = nex = 0
    
    currentIndex = 0
    indexHigher = len(objectsList)-1 #control the higher limit of list
    
    found = False
    prevFound = False
    nexFound = False
    
    #main logic:
    for currentValue in objectsList: #getting each value of list
        if currentValue == foo:
            found = True
            if currentIndex > 0: #check if target value is in the first position   
                prevFound = True
                prev = objectsList[currentIndex-1]
            if currentIndex < indexHigher: #check if target value is in the last position
                nexFound = True
                nex = objectsList[currentIndex+1]
            break #I am considering that target value only exist 1 time in the list
        currentIndex+=1
    
    if found:
        print("Value %s found" % foo)
        if prevFound:
            print("Previous Value: ", prev)
        else:
            print("Previous Value: Target value is in the first position of list.")
        if nexFound:
            print("Next Value: ", nex)
        else:
            print("Next Value: Target value is in the last position of list.")
    else:
        print("Target value does not exist in the list.")

-1

Pythonicでエレガントな方法:

objects = [1, 2, 3, 4, 5]
value = 3
if value in objects:
   index = objects.index(value)
   previous_value = objects[index-1]
   next_value = objects[index+1] if index + 1 < len(objects) else None

1
valueが最後の場合は失敗します。また、最初の要素であるprevious_valueかのように最後の要素を返しますvalue
Trang Oul 2016

それはあなたの要件に依存します。以下からの負のインデックスはprevious_value 、リストから最後の要素を返し、next_value引き上げるIndexErrorとthatsのエラー
はImportError

私はかなり時々このメソッドを探していました..今私はそれを手に入れました.. @ ImportErrorに感謝します。今私は...うまく私のスクリプトを拡張することができます
アザム

制限:でvalue複数回発生する可能性がありますobjectsが、を使用.index()すると、最初の発生(または発生しない場合はValueError)のみが検出されます。
SMCI
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.