0

What's the 'correct' python way of doing something to the first element, but something else to the second, and something different again for the third in a list, then repeat, eg:

a = [2, "foo1", "bar1", 5, "foo3", "bar2", 3, "foo2", "bar3"]
my_function(a)

should give

20
foo1
foobar1
50
foo3
foobar2
30
foo2
foobar3

where "my_function" would be something like:

def my_function(a):
    i = 0
    for line in a:
        if i == 0:
            line = line*10
        if i == 2:
            line = "foo"+line
        i = i + 1
        if i == 3:
            i = 0
        print line

But this looks very 'unpython'. Is there a better way of doing this, without an int to keep track? A way of saying, the first time I call this function, do this but the second time, do this, then the third time, do this, then go back to the beginning and do what you did first. A function that keeps track of how many times it has been called.

Jamie Twells
  • 1,924
  • 4
  • 26
  • 56

7 Answers7

3

Working with an iterator, you can do like this:

def my_function(a):
    a = iter(a)
    while True:
        yield 10 * next(a)    
        yield next(a)
        yield "foo" + next(a)

a = [2, "foo1", "bar1", 5, "foo3", "bar2", 3, "foo2", "bar3"]    
print list(my_function(a))
#prints [20, 'foo1', 'foobar1', 50, 'foo3', 'foobar2', 30, 'foo2', 'foobar3'] 

If anyone is wondering what happens at the end of the list, next(a) will raise StopIteration. The exception terminates the generator my_function, but code that iterates over it -- in this case list() -- will recognize it as a normal end of iteration.

Janne Karila
  • 24,266
  • 6
  • 53
  • 94
  • yep, was about to suggest this. I'd move `iter` to inside `my_function` though. – georg Jun 11 '14 at 09:37
  • I should have thought of this ... *facepalm*. Although I think I would move the `iter` inside the function. No reason to keep it out as far as I can see... +1 anyway. – mgilson Jun 11 '14 at 09:37
  • @thg435 mgilson: OK, inside the function it is. – Janne Karila Jun 11 '14 at 09:50
  • Now that the iterator is inside the function I can see what this is doing, and this is the sort of solution I was thinking of when I asked the question, so accepted! Thank you for your answer. – Jamie Twells Jun 11 '14 at 10:10
1

I might do something like:

def my_function(lst):
    items = (lst[i:i+3] for i in xrange(0, len(lst), 3))
    for group in items:
       yield group[0] * 10
       yield group[1]
       yield 'foo' + group[2]

Running on your input:

>>> list(my_function(a))
[20, 'foo1', 'foobar1', 50, 'foo3', 'foobar2', 30, 'foo2', 'foobar3']

I've made 2 big assumptions here -- that your list is indeed a list (or at least a sequence), and that the length of the sequence is divisible by 3 (otherwise you'll end up with an IndexError). Both of these assumptions could be taken care of without too much effort, but I'll leave that as an exercise if you're really interested ;-)

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • I like this, yeild seems like the right way to go, though I don't fully understand it yet. Could you explain the second line of your function a little bit for me? That seems to be where the magic happens. – Jamie Twells Jun 11 '14 at 09:33
  • First look at `xrange` -- It will yield every 3rd number starting at 0 up to (but not including) the length of the list. so..., `0, 3, 6, 9, ...`. From there it's just a simple slice to pull off 3 elements. `lst[0:3], lst[3:6], lst[6:9], ...`. Those lists of 3 elements are the `group` that gets pulled out of the `items` generator. – mgilson Jun 11 '14 at 09:35
  • Ah yes, that's very good. Not sure which of the answers is the best though, they're all good suggestions. – Jamie Twells Jun 11 '14 at 09:39
0

You could use something like:

def my_function (a):
    actions = [lambda l: l * 10,
               lambda l: l,
               lambda l: "foo" + l]
    for i, line in enumerate(a):
        line = actions[i % 3](line)
        print line
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
0

Other option to the existing ones using compination of zip and iter (explenation here):

def my_function2(a):
    for v1,v2,v3 in zip(*[iter(a)]*3):
        print(v1*10,v2, 'foo'+ v3)

% gives
20 foo1 foobar1
50 foo3 foobar2
30 foo2 foobar3
Community
  • 1
  • 1
Marcin
  • 215,873
  • 14
  • 235
  • 294
0

Use itertools for this, I find it most elegant and resilient to the number of elements/actions:

import itertools


def my_function(a):
    actions = itertools.cycle(
        (lambda l: l * 10, lambda l: l, lambda l: "foo" + l)
    )

    for val, action in itertools.izip(a, actions):
        yield action(val)

a = [2, "foo1", "bar1", 5, "foo3", "bar2", 3, "foo2", "bar3", 7]
print list(my_function(a))

Result:

[20, 'foo1', 'foobar1', 50, 'foo3', 'foobar2', 30, 'foo2', 'foobar3', 70]

As you can see from the example, this will work even if the elements count is not a multiplication of the actions count (or even less than actions count).

This is python2 version, if you need 3 please specify.

mkriheli
  • 1,788
  • 10
  • 18
0

Assuming a list as an input to the function,

def my_function(a):
   a[::3] = map(lambda x: 10*x, a[::3])
   a[2::3] = map(lambda x: "foo"+x, a[2::3])

Here I am making use of extended slices in python. This is more of a pythonic way i think.

Aashish P
  • 1,894
  • 5
  • 22
  • 36
0

This is C like way rather than Pythonic. So if you know C you can understand it just a few seconds.

def my_function(a):
    for n, line in enumerate(a):
        if n % 3 == 0:
            line = line * 10
        elif n % 3 == 2:
            line = "foo" + line
        print line
Kei Minagawa
  • 4,395
  • 3
  • 25
  • 43