views:

122

answers:

4

HI, I'm trying to use for loop to find the difference between every two object by minus each other. So, how can I find the next value in a for loop?

for entry in entries:
    first = entry      # Present value
    last = ??????      # The last value how to say?
    diff = last = first
+6  A: 

It should be noted that none of these solutions work for generators. For that see Glenn Maynards superior solution.

use zip for small lists:

 for current, last in zip(entries[1:], entries):
     diff = current - last

This makes a copy of the list (and a list of tuples from both copies of the list) so it's good to use itertools for handling larger lists

import itertools as it

items = it.izip(it.islice(entries, 1, None), entries)
for current, last in items:
    diff = current - last

This will avoid both making a copy of the list and making a list of tuples.

Another way to do it without making a copy is

entry_iter = iter(entries)
entry_iter.next() # Throw away the first version
for i, entry in enumerate(entry_iter):
    diff = entry - entries[i]

And yet another way is:

for i in xrange(len(entries) - 1):
    diff = entries[i+1] - entries[i]

This creates an iterator that indexes entries and advances it by one. It then uses enumerate to get an indice with the item. The indice starts at 0 and so points to the previous element because we the loop one item in.

Also, as Tyler pointed out in the comment, a loop might be overkill for such a simple problem if you just want to iterate over the differences.

diffs = (current - last for current, last in 
         it.izip(it.islice(entries, 1, None), entries))
aaronasterling
Awesome!!!!!!!!
Personally, I don't recommend nesting itertools for cases like this: it's not very intuitive and takes a bit of squinting to figure out what it's doing. Writing a simple generator function is much more instantly understandable. Note that this doesn't work for iterators (you could probably work around that with `itertools.tee` if you really wanted to).
Glenn Maynard
good point about the the iterators. I was temporarily confused with the comment I put on your answer. I guess intuitive in is in the eyes of the beholder.
aaronasterling
A useful test case for iterator handling: `entries = itertools.cycle([1,2,3,4])`
Glenn Maynard
Another one to add to my growing list of accepted-the-wrong-answer--no offense to you, Aaron. A critically faulty design assumption this site makes is that the person who asks a question is qualified to determine which answer is the best.
Glenn Maynard
@Glenn For what it's worth, I agree. I had deleted my answer but saw inferior duplicates popping up so I thought that if I undeleted it, they would stop. My mistake was thinking that putting a note that your answer was better would suffice. Maybe next time, I'll try to see if the <marquee> and <blink> tags still work.
aaronasterling
+2  A: 

zip works for lists, but for the general case:

def pairs(it):
    it = iter(it)
    prev = next(it)
    for v in it:
        yield prev, v
        prev = v

a = [1,2,3,4,5]
for prev, cur in pairs(a):
    print cur - prev

import itertools as it
for prev, cur in pairs(it.cycle([1,2,3,4])):
    print cur - prev

This works efficiently for large containers, and more importantly, it works for iterators:

for prev, cur in pairs(open("/usr/share/dict/words").xreadlines()):
    print cur, prev,

Edit: I changed the generator to omit the first value with no previous value, since that fits the original question better ("finding differences between adjacent pairs"), and I added an example case showing that it works for an infinitely-repeating iterator.

Glenn Maynard
I think it would be cleaner to drop the first element of `it` within the generator rather than doing that in the for loop.
intuited
@intuitive: I did do that, but in the general case of "give me access to the previous value" that may not be the ideal behavior. Anyhow, it's up to the user.
Glenn Maynard
A: 

very simply, using enumerate, no fancy stuff

>>> entries=[10,20,30,40]
>>> for n,i in enumerate(entries):
...     try:
...        print entries[n+1] - i
...     except IndexError:
...        print entries[-1]
...
10
10
10
40
ghostdog74
this is a terrible answer because [a] you're going to hit that except statement on every use (which is expensive) and [b] (like my deleted answer) it precludes the use of general sequences which can kill efficiency for large sections of code that can no longer pass in generators but must create lists. (also necessitating that client code know about the inner workings of whatever function this goes in and hence thoroughly defeating the point of functions)
aaronasterling
expensive?? how cheap do you want it to be? super cheap? dirt cheap? moderately cheap? Its going to only hit the except statement only once (if it occurs) during each use. I like it as its simple to understand and no fancy stuff.
ghostdog74
No, it's going to hit it everytime (on the last iteration when `n+1` isn't defined). Also, your use of `enumerate` is very unnatural here. what you want is `for i in range(len(entries) - 1): print entries[i+1] - entries[i]` __That__ is simple to understand and has no fancy stuff. But it still doesn't work on general sequences.
aaronasterling
NO, its going to hit last iteration 1 time. That's what i meant. And NO, using enumerate is not unnatural. Its also a generator. With your method, you invoke range() AND len(), which is "unnatural" to me.
ghostdog74
Like I said, it raises an exception on every use. `enumerate` is unnatural here because you are only using to get the index and not using the actual value (you could at least use the idiomatic `_` instead of the very confusing `i` as a placeholder for the value). `[x]range(len(entries) - 1)` completely avoids the exception and clearly announces that the intent is to loop _by index_ which is what you're doing.
aaronasterling
So what?? you use range() and len() and then do a maths subtraction. To me, you are doing extra work as well.. And if i want to use `i`, i can replace `entries[n]` with it. No big deal. So you gonna b*tch further about this? Or can we stop?
ghostdog74
@ghostdog74: Doing for `index, value in enumerate(seq):` and then never using `value` is unnatural. Using `n` for the index and `i` for the value is unnatural ** 2.
John Machin
@john machin, there you go, I have used `i`. Satisfied? whether using `n,i` is unnatural or not, I leave it to OP. He should be wise enough to use proper names for his variables. You guys like to b*tch a lot don't you.
ghostdog74
+1  A: 

i don't know exactly what are you looking but maybe this can help :

first = entries[0]
for entry in entries[1:]:
    last = entry       
    diff = last - first 
    first = entry
singularity