This question already has answers here:
Flatten an irregular (arbitrarily nested) list of lists
(51 answers)
Closed 8 years ago.
I have a set Elements that have multiple other elements nested inside of them. I am trying to extract all of them through recursion since I don't know how many levels deep does the nesting go. To compare this to something more pythonic i would say imagine a list of elements. Each item on that list can either be a single value or another list of elements. Then for each sub-list there can be either a single value or more sub-lists. I want to go through all of them and pull out each element from all of the list until the last of sub-lists have nothing but single items on it.
lst = [1,[[2,3,4],[5,6,7]],[[8,9,10],[[11,12,13],[14,15,16]]],17,18]
for i in lst:
subElem = i.GetSubComponentIds()
if subElem.Count >= 1:
idsList.append(subElem)
for i in subElem:
subElem2 = i.GetSubComponentIds():
if subElem2.Count = >= 1:.... and so on
How would I set up a recursive function that would grab every element on a input list run a GetSubComponentIds() function on it (that either returns another list or nothing). if the return is a list then run the same function GetSubComponentsIds() on each item of that sublist until you get nothing in return. At the same time for those that returned nothing i want that Id appended. So if i used the lst from the example above i would end up with a list of all of the elements 1-18 (the only trick is that i donk know how many sub lists deep each element on the original list is).
As I understand it, you want to use recursion to extract the elements buried in some nested object. Here is one method:
def is_list(x):
# Replace this with an appropriate test for your type
return hasattr(x, 'index')
def recurse(lst):
if is_list(lst):
elements = []
for element in lst:
elements += recurse(element)
return elements
else:
return [lst]
Run on your sample list:
>>> recurse(lst)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
Please refer to following code working with regular lists:
def flattern(lst, res):
for elem in lst:
if isinstance(elem, list):
flattern(elem, res)
else:
res.append(elem)
Please update it to using your functions
Related
def total_sales(sales_table, product):
if product in sales_table[0]:
return sum(list(zip(*sales_table[1:]))[sales_table[0].index(product)])
return 'Product not found'
Hi could someone explain 1. what the * is doing after the zip() and 2. why there is no comma or break between the two elements in the sum()
many thanks
At first there the * is for unpacking the list next to it, you can check the python syntax for the * and learn how it's used, and the other question u had, There are no comma in sum() because of it have a list() inside it, And sum() takes a list at sums it's up and return the sum of it.
To iterate through nested lists, I usually use For loops.
Check this site for more info about ways to iterate through lists in python:
https://www.geeksforgeeks.org/iterate-over-a-list-in-python/
To get the index of a variable in a list (after iterating correctly through all your nested lists), use .index(element, start, end). This will return the index of the desired element you are searching for.
For more information about .index()
https://www.programiz.com/python-programming/methods/list/index
The zip() function returns a zip object, which is an iterator of tuples where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together etc.
If the passed iterators have different lengths, the iterator with the least items decides the length of the new iterator.
https://realpython.com/python-zip-function/#:~:text=Python's%20zip()%20function%20is,%2C%20sets%2C%20and%20so%20on.
The sum(iterable, start) function returns a number, the sum of all items in an iterable.
https://www.programiz.com/python-programming/methods/built-in/sum
Example:
numbers = [2.5, 3, 4, -5]
# start parameter is not provided
numbers_sum = sum(numbers)
print(numbers_sum)
# start = 10
numbers_sum = sum(numbers, 10)
print(numbers_sum)
# 4.5
# 14.5
The * can also be used for unpacking the containers. The easiest example is that we have data in the form of a list, tuple or dict, and a function take variable arguments:
from functools import reduce
primes = [2, 3, 5, 7, 11, 13]
def product(*numbers):
p = reduce(lambda x, y: x * y, numbers)
return p
product(*primes)
# 30030
product(primes)
# [2, 3, 5, 7, 11, 13]
Because the product() take the variable arguments, we need to unpack the our list data and pass it to that function. In this case, if we pass the primes as *primes, every elements of the primes list will be unpacked, then stored in list called numbers. If pass that list primes to the function without unpacking, the numbers will has only one primes list not all elements of primes.
This question already has answers here:
Optimal method to find the max of sublist items within list
(3 answers)
Closed 4 years ago.
I have a nested list and I want to find the largest value stored in a specific index of the nested list.
For example: data = [[12345678, 14, 1],[135763365, 12, 0],[1234, 9, 0]]
I want to return the largest value stored in index 0 of the list data: 135763365.
How can I return this? I don't want to brute force figure out the largest myself and return that value. I want to write a function that will return this value in a nested list.
You can use a list comprehension and apply max() function over result.
max_number = max([item[0] for item in data])
Output
135763365
Another approach is to specify key in the max function.
max(data, key=operator.itemgetter(0))[0]
Using max and lambda:
>>> data = [[12345678, 14, 1],[135763365, 12, 0],[1234, 9, 0]]
>>> max(data, key=lambda x: x[0])[0]
135763365
This question already has answers here:
How to modify list entries during for loop?
(10 answers)
Closed 5 months ago.
I know you should not add/remove items while iterating over a list. But can I modify an item in a list I'm iterating over if I do not change the list length?
class Car(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return type(self).__name__ + "_" + self.name
my_cars = [Car("Ferrari"), Car("Mercedes"), Car("BMW")]
print(my_cars) # [Car_Ferrari, Car_Mercedes, Car_BMW]
for car in my_cars:
car.name = "Moskvich"
print(my_cars) # [Car_Moskvich, Car_Moskvich, Car_Moskvich]
Or should I iterate over the list indices instead? Like that:
for car_id in range(len(my_cars)):
my_cars[car_id].name = "Moskvich"
The question is: are the both ways above allowed or only the second one is error-free?
If the answer is yes, will the following snippet be valid?
lovely_numbers = [[41, 32, 17], [26, 55]]
for numbers_pair in lovely_numbers:
numbers_pair.pop()
print(lovely_numbers) # [[41, 32], [26]]
UPD. I'd like to see the python documentation where it says "these operations are allowed" rather than someone's assumptions.
You are not modifying the list, so to speak. You are simply modifying the elements in the list. I don't believe this is a problem.
To answer your second question, both ways are indeed allowed (as you know, since you ran the code), but it would depend on the situation. Are the contents mutable or immutable?
For example, if you want to add one to every element in a list of integers, this would not work:
>>> x = [1, 2, 3, 4, 5]
>>> for i in x:
... i += 1
...
>>> x
[1, 2, 3, 4, 5]
Indeed, ints are immutable objects. Instead, you'd need to iterate over the indices and change the element at each index, like this:
>>> for i in range(len(x)):
... x[i] += 1
...
>>> x
[2, 3, 4, 5, 6]
If your items are mutable, then the first method (of directly iterating over the elements rather than the indices) is more efficient without a doubt, because the extra step of indexing is an overhead that can be avoided since those elements are mutable.
I know you should not add/remove items while iterating over a list. But can I modify an item in a list I'm iterating over if I do not change the list length?
You're not modifying the list in any way at all. What you are modifying is the elements in the list; That is perfectly fine. As long as you don't directly change the actual list, you're fine.
There's no need to iterate over the indices. In fact, that's unidiomatic. Unless you are actually trying to change the list itself, simply iterate over the list by value.
If the answer is yes, will the following snippet be valid?
lovely_numbers = [[41, 32, 17], [26, 55]]
for numbers_pair in lovely_numbers:
numbers_pair.pop()
print(lovely_numbers) # [[41, 32], [26]]
Absolutely. For the exact same reasons as I said above. Your not modifying lovely_numbers itself. Rather, you're only modifying the elements in lovely_numbers.
Examples where the list is modified and not during while iterating over the elements of the list
list_modified_during_iteration.py
a = [1,2]
i = 0
for item in a:
if i<5:
print 'append'
a.append(i+2)
print a
i += 1
list_not_modified_during_iteration.py (Changed item to i)
a = [1,2]
i = 0
for i in range(len(a)):
if i<5:
print 'append'
a.append(i+2)
print a
i += 1
Of course, you can. The first way is normal, but in some cases you can also use list comprehensions or map().
I was trying to write a piece of program that will remove any repeating items in the list, but I get a list index out of range
Here's the code:
a_list = [1, 4, 3, 2, 3]
def repeating(any_list):
list_item, comparable = any_list, any_list
for x in any_list:
list_item[x]
comparable[x]
if list_item == comparable:
any_list.remove(x)
print(any_list)
repeating(a_list)
So my question is, what's wrong?
Your code does not do what you think it does.
First you are creating additional references to the same list here:
list_item, comparable = any_list, any_list
list_item and comparable are just additional names to access the same list object.
You then loop over the values contained in any_list:
for x in any_list:
This assigns first 1, then 4, then 3, then 2, then 3 again to x.
Next, use those values as indexes into the other two references to the list, but ignore the result of those expressions:
list_item[x]
comparable[x]
This doesn't do anything, other than test if those indexes exist.
The following line then is always true:
if list_item == comparable:
because the two variables reference the same list object.
Because that is always true, the following line is always executed:
any_list.remove(x)
This removes the first x from the list, making the list shorter, while still iterating. This causes the for loop to skip items as it'll move the pointer to the next element. See Loop "Forgets" to Remove Some Items for why that is.
All in all, you end up with 4, then 3 items in the list, so list_item[3] then fails and throws the exception.
The proper way to remove duplicates is to use a set object:
def repeating(any_list):
return list(set(any_list))
because a set can only hold unique items. It'll alter the order however. If the order is important, you can use a collections.OrderedDict() object:
def repeating(any_list):
return list(OrderedDict.fromkeys(any_list))
Like a set, a dictionary can only hold unique keys, but an OrderedDict actually also keeps track of the order of insertion; the dict.fromkeys() method gives each element in any_list a value of None unless the element was already there. Turning that back in to a list gives you the unique elements in a first-come, first serve order:
>>> from collections import OrderedDict
>>> a_list = [1, 4, 3, 2, 3]
>>> list(set(a_list))
[1, 2, 3, 4]
>>> list(OrderedDict.fromkeys(a_list))
[1, 4, 3, 2]
See How do you remove duplicates from a list in whilst preserving order? for more options still.
The easiest way to solve your issue is to convert the list to a set and then, back to a list...
def repeating(any_list):
print list(set(any_list))
You're probably having an issue, because you're modifying the list (removing), while iterating over it.
If you want to remove duplicates in a list but don't care about the elements formatting then you can
def removeDuplicate(numlist):
return list(set(numlist))
If you want to preserve the order then
def removeDuplicate(numlist):
return sorted(list(set(numlist)), key=numlist.index)
This question already has answers here:
python : list index out of range error while iteratively popping elements
(12 answers)
How to remove items from a list while iterating?
(25 answers)
Closed 6 years ago.
for i in range(len(lst)):
if lst[i][0]==1 or lst[i][1]==1:
lst.remove(lst[i])
return lst
This gives "IndexError: list index out of range" Why is this happening?
You're modifying the list you're iterating over. If you do that, the size of the list shrinks, so eventually lst[i] will point beyond the list's boundaries.
>>> lst = [1,2,3]
>>> lst[2]
3
>>> lst.remove(1)
>>> lst[1]
3
>>> lst[2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
It's safer to construct a new list:
return [item for item in lst if item[0]!=1 and item[1]!=1]
You shouldn't remove items from the list as you iterate over it; that changes the indices of all subsequent items, hence the IndexError. You could try a simple list comprehension:
lst = [item for item in lst if (item[0] != 1 and item[1] != 1)]
Generally it means that you are providing an index for which a list element does not exist.
E.g, if your list was [12, 32, 50, 71], and you asked for the element at index 10, you would be well out of bounds and receive an error, as only elements 0 through 3 exist.
The problem is that you remove items in the list which reduces its size. What you have to do is make an array with the indexes you want to remove and remove them backwards.
Another way would be to create a temporary list that you would add the elements you don't want to delete and then overwrite your initial list with the list containing all the elements you want to keep.
By process of deduction I have concluded that your lst looks something like this:
lst = [ ...[val1, val2], [val1, val2], [val1, val2]... ]
I think what happened here is you confused your 'for i in range', with a 'for i in' (I have done this many times also.)
The line where your error is occurring is:
lst.remove(lst[i])
You can correct this by simply changing your code like so:
for i in lst:
if i[0] ==1 or i[1] ==1:
lst.remove(lst[i])
return lst
The way your code was structured before list[i] didn't make any sense, your i was a number greater than the number of two-value-items in lst.
=D