pop/remove items out of a python tuple
Asked Answered
B

10

49

I am not sure if I can make myself clear but will try.

I have a tuple in python which I go through as follows (see code below). While going through it, I maintain a counter (let's call it 'n') and 'pop' items that meet a certain condition.

Now of course once I pop the first item, the numbering all goes wrong, how can I do what I want to do more elegantly while removing only certain entries of a tuple on the fly?

for x in tupleX:
  n=0
  if (condition):
     tupleX.pop(n)
  n=n+1
Baptistery answered 10/2, 2014 at 16:36 Comment(2)
tuples are immutable, and don't have a pop method. Are you really talking about a list?Caliber
@Caliber was correct 6 years ago, but Python 3 allows slicing on tuples, so one can effectively pop.Inrush
B
4

ok I figured out a crude way of doing it.

I store the "n" value in the for loop when condition is satisfied in a list (lets call it delList) then do the following:

    for ii in sorted(delList, reverse=True):
    tupleX.pop(ii)

Any other suggestions are welcome too.

Baptistery answered 10/2, 2014 at 16:43 Comment(0)
E
83

As DSM mentions, tuple's are immutable, but even for lists, a more elegant solution is to use filter:

tupleX = filter(str.isdigit, tupleX)

or, if condition is not a function, use a comprehension:

tupleX = [x for x in tupleX if x > 5]

if you really need tupleX to be a tuple, use a generator expression and pass that to tuple:

tupleX = tuple(x for x in tupleX if condition)
Eaddy answered 10/2, 2014 at 16:45 Comment(4)
For others' reference, the generator expression is optimal in terms of speed.Cymograph
@Alecg_O: can you provide benchmarks for that claim? In fact, in the case that the condition is an (unapplied) C function (as in the example I provided), filter is usually faster; generator expressions must execute some python bytecode, but aren't required to create a call-frame (also as in my example for the list comprehension).Eaddy
According to the docs, the filter and generator solutions are functionally identical - both return an iterable of the original filtered by condition, and by this definition both are constant time. However, assuming the OP requires a tuple for output, the difference comes in the conversion back. For proof: take tuple() off the generator and they'll both return instantly, but tuple() the filter and it takes longer proportional to the size of tupleX.Cymograph
Interesting sidenote: I came across this post while looking for the most efficient way to "remove" a single item from a tuple. While these are all great solutions, ironically "x = list(x); x.remove(<item>); x = tuple(x)" is faster than any of them :PCymograph
O
18

Yes we can do it. First convert the tuple into an list, then delete the element in the list after that again convert back into tuple.

Demo:

my_tuple = (10, 20, 30, 40, 50)

# converting the tuple to the list
my_list = list(my_tuple)
print my_list  # output: [10, 20, 30, 40, 50]

# Here i wanna delete second element "20"
my_list.pop(1) # output: [10, 30, 40, 50]
# As you aware that pop(1) indicates second position

# Here i wanna remove the element "50"
my_list.remove(50) # output: [10, 30, 40]

# again converting the my_list back to my_tuple
my_tuple = tuple(my_list)


print my_tuple # output: (10, 30, 40)

Thanks

Outlive answered 4/10, 2017 at 9:58 Comment(1)
It's a terrible method. You will allocate a list, perform an action and then allocate another tuple. Way faster and elegant would be to create a new tuple without the elementMandibular
I
10

In Python 3 this is no longer an issue, and you really don't want to use list comprehension, coercion, filters, functions or lambdas for something like this.

Just use

popped = unpopped[:-1]

Remember that it's an immutable, so you will have to reassign the value if you want it to change

my_tuple = my_tuple[:-1]

Example

>>> foo= 3,5,2,4,78,2,1
>>> foo
(3, 5, 2, 4, 78, 2, 1)
foo[:-1]
(3, 5, 2, 4, 78, 2)

If you want to have the popped value,,

>>> foo= 3,5,2,4,78,2,1
>>> foo
(3, 5, 2, 4, 78, 2, 1)
>>> foo, bit = foo[:-1], foo[-1]
>>> bit
1
>>> foo
(3, 5, 2, 4, 78, 2)

Or, to work with each value of a tuple starting at the back...

foo = 3,5,2,4,78,2,1
for f in reversed(foo):
    print(f)  # 1; 2; 78; ...

Or, with the count...

foo = 3,5,2,4,78,2,1
for f, i in enumerate(reversed(foo)):
    print(i, f)  # 0 1; 1 2; 2 78; ...

Or, to coerce into a list..

bar = [*foo]
#or 
bar = list(foo)
Inrush answered 1/2, 2021 at 16:40 Comment(0)
B
4

ok I figured out a crude way of doing it.

I store the "n" value in the for loop when condition is satisfied in a list (lets call it delList) then do the following:

    for ii in sorted(delList, reverse=True):
    tupleX.pop(ii)

Any other suggestions are welcome too.

Baptistery answered 10/2, 2014 at 16:43 Comment(0)
O
3

Maybe you want dictionaries?

d = dict( (i,value) for i,value in enumerate(tple))
while d:
    bla bla bla
    del b[x]
Organzine answered 10/2, 2014 at 16:43 Comment(2)
no I have to work with a tuple as its given to me by an external function that I can't edit-old hierarchical code with too many dependancies.Baptistery
thank you, I will try this -more elegant than my method.Baptistery
F
1

A shorter way perhaps:

tup = (0, 1, 2, 3)
new_tup = (*tup[:-2], tup[-1])
print(new_tup) # (0, 1, 3)
Fruitless answered 13/10, 2022 at 11:15 Comment(0)
P
0

There is a simple but practical solution.

As DSM said, tuples are immutable, but we know Lists are mutable. So if you change a tuple to a list, it will be mutable. Then you can delete the items by the condition, then after changing the type to a tuple again. That’s it.

Please look at the codes below:

tuplex = list(tuplex)
for x in tuplex:
  if (condition):
     tuplex.pop(tuplex.index(x))
tuplex = tuple(tuplex)
print(tuplex)

For example, the following procedure will delete all even numbers from a given tuple.

tuplex = (1, 2, 3, 4, 5, 6, 7, 8, 9)
tuplex = list(tuplex)
for x in tuplex:
  if (x % 2 == 0):
     tuplex.pop(tuplex.index(x))
tuplex = tuple(tuplex)
print(tuplex)

if you test the type of the last tuplex, you will find it is a tuple.

Finally, if you want to define an index counter as you did (i.e., n), you should initialize it before the loop, not in the loop.

Phylactery answered 22/8, 2017 at 17:51 Comment(0)
D
0

The best solution is the tuple applied to a list comprehension, but to extract one item this could work:

def pop_tuple(tuple, n):
    return tuple[:n]+tuple[n+1:], tuple[n]
Dally answered 21/5, 2020 at 16:12 Comment(0)
L
0

say you have a dict with tuples as keys, e.g: labels = {(1,2,0): 'label_1'} you can modify the elements of the tuple keys as follows:

formatted_labels = {(elem[0],elem[1]):labels[elem] for elem in labels}

Here, we ignore the last elements.

Longboat answered 14/7, 2020 at 10:5 Comment(0)
W
0

One solution is to convert to set and bring back to tuple

tupleX = (
    "ZAR",
    "PAL",
    "SEV",
    "ALC",
    "LPA",
    "TFN",)

remove = (
    "LPA",
    "TFN",)

tuple(set(tupleX) - set(remove))

('ZAR', 'PAL', 'ALC', 'SEV')

Wingard answered 19/7, 2022 at 7:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.