﻿ Apply function to one element of a list in Python - Oipapio- oipapio.com

# Apply function to one element of a list in Python

Question:

I'm looking for a concise and functional style way to apply a function to one element of a tuple and return the new tuple, in Python.

For example, for the following input:

``````inp = ("hello", "my", "friend")
``````

I would like to be able to get the following output:

``````out = ("hello", "MY", "friend")
``````

I came up with two solutions which I'm not satisfied with.

One uses a higher-order function.

``````def apply_at(arr, func, i):
return arr[0:i] + [func(arr[i])] + arr[i+1:]

apply_at(inp, lambda x: x.upper(), 1)
``````

One uses list comprehensions (this one assumes the length of the tuple is known).

``````[(a,b.upper(),c) for a,b,c in [inp]][0]
``````

Is there a better way? Thanks!

I commented in support of your first snippet, but here are a couple other ways for the record:

``````(lambda (a,b,c): [a,b.upper(),c])(inp)
``````

(Won't work in Python 3.x.) And:

``````[inp[0], inp[1].upper(), inp[1]]
``````

Here is a version that works on any iterable and returns a generator:

``````>>> inp = ("hello", "my", "friend")
>>> def apply_nth(fn, n, iterable):
...    return (fn(x) if i==n else x for (i,x) in enumerate(iterable))
...
>>> tuple(apply_nth(str.upper, 1, inp))
('hello', 'MY', 'friend')
``````

You can extend this so that instead of one position you can give it a list of positions:

``````>>> def apply_at(fn, pos_lst, iterable):
...    pos_lst = set(pos_lst)
...    return (fn(x) if i in pos_lst else x for (i,x) in enumerate(iterable))
...
>>> ''.join(apply_at(str.upper, [2,4,6,8], "abcdefghijklmno"))
'abCdEfGhIjklmno'
``````

``````>>> inp = "hello", "my", "friend"
>>> index = 1
>>> inp[:index] + ( str.upper(inp[index]),) + inp[index + 1:]
('hello', 'MY', 'friend')
``````

Seems simple, the only thing you may need to know is that to make a single element tuple, do (elt,)

Maybe some' like this?

``````>>>inp = ("hello", "my", "friend")
>>>out =  tuple([i == 1 and x.upper() or x for (x,i) in zip(t,range(len(t)))])

>>> out
('hello', 'MY', 'friend')
``````

Note: rather than `(x,i) in zip(t, range(len(t)))` I should have thought of using the enumerate function : `(i,x) in enumerate(t)`

Making it a bit more general:
Rather than hard-coding the 1, we can place it in a variable.
Also, by using a tuple for that purpose, we can apply the function to elements at multiple indexes.

``````>>>inp = ("hello", "my", "friend")
>>>ix  = (0,2)
>>>out =  tuple([i in ix and x.upper() or x for (i, x) in enumerate(t)])

>>> out
('HELLO', 'my', 'FRIEND')
``````

Also, we can "replace" the zip()/enumerate() by map(), in something like

``````out = tuple(map(lambda x,i : i == 1 and x.upper() or x, inp, range(len(inp)) ) )
``````

Could be something as simple as:

``````>>> f = str.upper  # or whatever function taking a single argument
>>> out = tuple(map(lambda x,i : i == 1 and f(x) or x, inp, range(len(inp)) ) )
``````

Since we're talking about applying any function, we should mention the small caveat with the `condition and if_true or if_false` construct which is not exactly a substitute for the if/else ternary operator found in other languages. The limitation is that the function cannot return a value which is equivalent to False (None, 0, 0.0, '' for example). A suggestion to avoid this problem, is, with Python 2.5 and up, to use the true if-else ternary operator, as shown in Dave Kirby's answer (note the `when_true if condition else when_false` syntax of this operator)

I don't understand if you want to apply a certain function to every element in the tuple that passes some test, or if you would like it to apply the function to any element present at a certain index of the tuple. So I have coded both algorithms:

This is the algorithm (coded in Python) that I would use to solve this problem in a functional language like scheme:

This function will identify the element identifiable by `id` and apply `func` to it and return a list with that element changed to the output of `func`. It will do this for every element identifiable as `id`:

``````def doSomethingTo(tup, id):
return tuple(doSomethingToHelper(list(tup), id))

def doSomethingToHelper(L, id):
if len(L) == 0:
return L
elif L[0] == id:
return [func(L[0])] + doSomethingToHelper(L[1:], id)
else:
return [L[0]] + doSomethingToHelper(L[1:], id)
``````

This algorithm will find the element at the index of the tuple and apply `func` to it, and stick it back into its original index in the tuple

``````def doSomethingAt(tup, i):
return tuple(doSomethingAtHelper(list(tup), i, 0))

def doSomethingAtHelper(L, index, i):
if len(L) == 0:
return L
elif i == index:
return [func(L[0])] + L[1:]
else:
return [L[0]] + doSomethingAtHelper(L[1:], index, i+1)
``````

i also like the answer that Dave Kirby gave. however, as a public service announcement, i'd like to say that this is not a typical use case for tuples -- these are data structures that originated in Python as a means to move data (parameters, arguments) to and from functions... they were not meant for the programmer to use as general array-like data structures in applications -- this is why lists exist. naturally, if you're needing the read-only/immutable feature of tuples, that is a fair argument, but given the OP question, this should've been done with lists instead -- note how there is extra code to either pull the tuple apart and put the resulting one together and/or the need to temporarily convert to a list and back.