Create an account


Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tut] The Most Pythonic Way to Remove Multiple Items From a List

#1
The Most Pythonic Way to Remove Multiple Items From a List

<div><p> <iframe src="https://trinket.io/embed/python/06c74bc9c3" width="100%" height="356" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe> </p>
<p>Python’s built-in list data structure has many powerful methods any advanced Python programmer must be familiar with. However, some operations on lists can’t be performed simply by calling the right method.</p>
<p>You can add a single item to a list using the method <code>append(item)</code> on the list. If you want to add a list of items to another list, there is the method <code>expand(items)</code> which does the job for you.</p>
<p>The same holds if you want to delete an item from a list, you simply call the method <code>remove(item)</code>and you get the desired outcome. </p>
<p><strong><em>But, did you ever wonder how to delete a list of items from a given list?</em></strong> Or what if the indices of the items to be deleted were given, how would you do that?</p>
<p>These were the questions I was asking myself in one of my latest hobby projects. Therefore I decided to find out the most Pythonic way to do that.</p>
<h2>Problem</h2>
<p>Let’s frame our problem like this: Given a list of Task items, <strong>how can we remove all items from the list </strong>which are marked as done?</p>
<p>Currently the implementation looks as follows:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class Task: def __init__(self, title): self.title = title self.done = False self.done_by = None def is_done(self): return self.done def set_done(self, name): self.done = True self.done_by = name def __repr__(self): state = f'was done by {self.done_by}' if self.done else 'is not done' s = f'Task: {self.title} {state}' return s todo_list = [ Task('Clean House'), Task('Walk Dog'), Task('Buy Bread'), Task('Repair Car'), Task('Plant Tree'), Task('Water Flowers'), Task('Bake Cake')
] todo_list[0].set_done('Bob')
todo_list[2].set_done('Alice')
todo_list[5].set_done('Bob') # print the whole list
print(todo_list)</pre>
<p>So, how can we clean up our todo list so that it contains only tasks that have not yet been done?</p>
<h2>Solutions</h2>
<p>The following solutions can be divided into two groups:</p>
<ol>
<li>Delete elements of given indices</li>
<li>Delete elements by a certain condition</li>
</ol>
<p>Any solution of the first type can also be used to delete the elements by a given condition. To accomplish this, all we have to do, is iterate once over the input list, check the condition and store the indices of the elements for which the condition was <code>True</code>. This can be implemented as follows:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">indices = []
for idx, task in enumerate(todo_list): if task.is_done(): indices.append(idx)</pre>
<p>Since it takes one iteration of the list to find the indices, this adds <em>O(n)</em> to the runtime complexity. Yet, since any solution has at least a time complexity of <em>O(n)</em>, we can neglect this first step.</p>
<h2>Method 1: Remove a Single Item From the List and Repeat in a Loop</h2>
<p>As mentioned before, there are methods to remove a single item from a <a href="https://blog.finxter.com/python-lists/" target="_blank" rel="noreferrer noopener" title="The Ultimate Guide to Python Lists">list</a>, either by value or by index.</p>
<p>Therefore one solution to remove several items is to use a method that removes a single item and executes it in a loop. Though, there is a pitfall to this solution. After we remove the element at index 0, all the other elements shift, and their indices change because the element at index 1 is now at index 0 and so on.</p>
<p>This is how the solution would look as code:</p>
<h3>1.1. Remove using pop()</h3>
<p>The <a href="https://blog.finxter.com/python-list-pop/" target="_blank" rel="noreferrer noopener" title="Python List pop()"><code>list.pop()</code> method</a> removes and returns the last element from an existing <code>list</code>. The <code>list.pop(index)</code> method with the optional argument <code>index</code> removes and returns the element at the position <code>index</code>.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">indices = [0, 2, 5] # must be ordered!
shift = 0
for i in indices: todo_list.pop(i-shift) shift += 1</pre>
<p>Well, probably this looks a bit awkward to you, and be reassured, it’s not the way you would do it in Python!</p>
<p>To avoid shifting, we can reverse sort the list of indices so that we can remove the items from end to start:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">indices = [0, 2, 5]
for i in sorted(indices, reverse=True): todo_list.pop(i)</pre>
<h3>1.2. Remove using remove()</h3>
<p>A slightly simpler solution, but still not the best solution, uses the <a href="https://blog.finxter.com/python-list-pop/" target="_blank" rel="noreferrer noopener" title="Python List pop()">method <code>re</code></a><code><a href="https://blog.finxter.com/python-list-remove/" target="_blank" rel="noreferrer noopener" title="Python List remove()">move(item)</a></code>.</p>
<p>We iterate over the list and check for each item if it satisfied the condition so that it can be deleted. This solution would look like this:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">for task in todo_list: if task.is_done(): todo_list.remove(task)</pre>
<p>Be careful if you use <code>remove(item)</code> on a list of simple data types like integers. The function <code>remove()</code> deletes the first occurrence of the given value from the list!</p>
<p>In all of the above solutions, we performed the deletion in-place, which means, we kept the initial instance of the list.</p>
<p>By now you should see, a good solution to the problem is not that obvious.</p>
<h3>1.3. Remove using itemgetter() and remove()</h3>
<p>If you use the function <code>itemgetter</code> from the module <code>operator</code> there is another interesting solution which is basically an improvement of solution 1.1.</p>
<p>The function <code>itemgetter</code> takes an arbitrary number of indices and returns all the elements from those indices in a tuple. Here is the implementation of the proposed solution:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">from operator import itemgetter indices = [0, 2, 5]
for item in (itemgetter(*idx)(todo_list)): xs.remove(item)</pre>
<p>But still, the code is more complex than it needs to be.</p>
<h2>Method 2. Remove Multiple Items from a List</h2>
<p>In the previous solutions, we simply adapted functionality for deleting a single element so that we could use it inside a <a href="https://blog.finxter.com/python-loops/" title="Python Loops" target="_blank" rel="noreferrer noopener">loop</a>. In this section, we take a look at more Pythonic solutions for the problem.</p>
<h3>2.1. Remove all elements from a list</h3>
<p>If you want to remove all elements from the list, there is a very simple solution: Use the <a href="https://blog.finxter.com/python-list-clear/" title="Python List clear()" target="_blank" rel="noreferrer noopener">list class’s method <code>clear()</code></a>. It removes all elements from the list in-place.</p>
<h3>2.2. Remove a slice from a list</h3>
<p>If your elements are in a continuous range or if they have a least equal distances from each other a simple way to delete multiple elements from a list is using the keyword <a href="https://blog.finxter.com/how-to-remove-an-element-from-a-python-list-by-index/" target="_blank" rel="noreferrer noopener" title="How to Remove an Element From a Python List by Index?"><code>del</code> </a>together with <a href="https://blog.finxter.com/introduction-to-slicing-in-python/" target="_blank" rel="noreferrer noopener" title="Introduction to Slicing in Python">slicing</a>.</p>
<p>This could look like this:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">del todo_list[1::2]</pre>
<p>It deletes the elements in-place, however, it doesn’t help if we want to delete randomly distributed elements from our <a href="https://blog.finxter.com/python-list-methods/" target="_blank" rel="noreferrer noopener" title="Python List Methods">list</a>.</p>
<h3>2.3. Remove randomly distributed elements from a list using set operations</h3>
<p>First, we iterate over the list once and extract all items to be deleted. Then, we <a href="https://blog.finxter.com/python-list-to-set/" target="_blank" rel="noreferrer noopener" title="Python List to Set Conversion [Interactive Guide]">convert both lists to sets</a> and perform the removal using <a href="https://blog.finxter.com/sets-in-python/" target="_blank" rel="noreferrer noopener" title="The Ultimate Guide to Python Sets – with Harry Potter Examples">set operations</a>. This looks as follows:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">done = []
for task in todo_list: if task.is_done(): done.append(task) todo_list = list(set(todo_list) - set(done))</pre>
<p>Under the hood, a set in Python is a hashmap that allows performing certain operations on sets very fast (<em>O(1)</em>). Unfortunately we have to <a href="https://blog.finxter.com/python-list-to-set/" title="Python List to Set Conversion [Interactive Guide]" target="_blank" rel="noreferrer noopener">convert from a list to a set</a> and <a href="https://blog.finxter.com/python-set-to-list/" title="Python Convert Set to List [Interactive Guide]" target="_blank" rel="noreferrer noopener">back</a>, so that we loose the advantage in speed. And again, we end up with an <em>O(n)</em> solution.</p>
<p><a href="https://blog.finxter.com/runtime-complexity-of-python-list-methods-easy-table-lookup/" target="_blank" rel="noreferrer noopener" title="Runtime Complexity of Python List Methods [Easy Table Lookup]">Fore more information about the computational complexity of Python operations, check out our detailed article about the topic.</a></p>
<p>This solution doesn’t work in-place and is a bit difficult to read due to the many conversions between data structures.</p>
<h3>2.4. Remove randomly distributed elements from a list using list comprehension</h3>
<p>The best way to do this in Python is actually very close to what we saw in the first section of this article where we iterated over the list and removed the elements for which a certain condition was True.</p>
<p>However, in this solution, we will proceed the other way round: We iterate over the old list and create a new list to which we add all the elements that we want to keep. Obviously, we have to create a new list to achieve this, so the solution won’t work in-place. </p>
<p>Python provides just what we need to get the desired result in one single line of code: <a href="https://blog.finxter.com/list-comprehension/" title="List Comprehension in Python — A Helpful Illustrated Guide" target="_blank" rel="noreferrer noopener">list comprehensions</a>.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">todo_list = [task for task in todo_list if not task.is_done()]</pre>
<p>If we assign the result of the list comprehension back to our initial <code>todo_list</code> variable, this variable will now point to a list that contains only tasks that weren’t done yet.</p>
<p>After the above line of code, the memory address to which the variable <code>todo_list</code> points has changed!</p>
<p>However, that’s how you should delete several elements from a list in Python. If you want to do this in-place, there is also a <a href="https://blog.finxter.com/python-one-liners-the-ultimate-collection/" target="_blank" rel="noreferrer noopener" title="Python One-Liners – The Ultimate Collection">one-line </a>solution to the problem, though, I personally wouldn’t recommend you to use this.</p>
<p>Here is the code:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[todo_list.remove(task) for task in todo_list if task.is_done()]</pre>
<p>Be honest, how long did you take to wrap your head around that?</p>
<p>We use a dummy list comprehension in which we delete the selected elements from the initial list, finally we throw away the list comprehension’s resulting list.</p>
<p>So, what we actually do is to abuse the list comprehension to iterate over <code>todo_list</code> and delete items from it.</p>
<h2>Conclusion</h2>
<p>Depending on the distribution of the items in the list, there are different solutions.</p>
<ol>
<li>If you want to remove all elements from a list, use the list’s method <code>clear()</code>.</li>
<li>If you want to remove a continuous range from the list or if you want to delete items with equal distances between, use slicing with the operator <code>del l[startConfusedtop]</code>.</li>
<li>If you want to remove randomly distributed elements, use a list comprehension which selects only the elements you want to keep – this is the solution I recommend.</li>
</ol>
<p>Obviously, there are more possibilities to solve the problem, yet, the solutions presented in this article are the most common ones and also the easiest to understand. If you find another great solution, feel free to <a href="https://www.instagram.com/codinglukas/" target="_blank" rel="noreferrer noopener" title="https://www.instagram.com/codinglukas/">contact us</a>! We would love to see it.</p>
<h2 id="block-874f81ac-037a-45a2-ad19-6d98c205c3bc">Where to Go From Here?</h2>
<p>Enough theory, let’s get some practice!</p>
<p>To become successful in coding, you need to get out there and solve real problems for real people. That’s how you can become a six-figure earner easily. And that’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?</p>
<p><strong>Practice projects is how you sharpen your saw in coding!</strong></p>
<p>Do you want to become a code master by focusing on practical code projects that actually earn you money and solve problems for people?</p>
<p>Then become a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.</p>
<p>Join my free webinar <a href="https://www.digistore24.com/content/261950/26036/lukasrieger/" target="_blank" rel="noreferrer noopener" title="https://www.digistore24.com/content/261950/26036/lukasrieger/">“How to Build Your High-Income Skill Python”</a> and watch how I grew my coding business online and how you can, too—from the comfort of your own home.</p>
<p><a href="https://www.digistore24.com/content/261950/26036/lukasrieger/" target="_blank" rel="noreferrer noopener" title="https://www.digistore24.com/content/261950/26036/lukasrieger/">Join the free webinar now!</a></p>
</div>


https://www.sickgaming.net/blog/2020/06/...om-a-list/
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

Forum software by © MyBB Theme © iAndrew 2016