there is an exercise I was assigned to solve a week ago and I can't seem to find a solution to it. I want to create a Python function which receives a list and returns every element inside the list in a new one (even if there are nested elements inside it).
def ListOfLists(lista):
listaUnica = []
if(type(lista) != list):
return None
for i in lista:
if (type(i) != list):
listaUnica.append(i)
elif(type(i) == list):
for elemento in i:
if(type(elemento) == list):
listaUnica.append(elemento)
continue
elif(type(elemento) != list):
listaUnica.append(elemento)
return listaUnica
This is the best function I came up with. The problem that I have with this function is that it doesn't analyse while looping though the list if an element is indeed a list, if it turns out true, loop through that nested list and so on until there are no more nested lists. While doing so I want it to return the elements in the same order that they were assigned originally.
Let's suppose:
lista = [1,2,[3, [4, 5, [6]], 7]
ListOfLists(lista)
Expected output:
[1, 2, 3, 4, 5, 6, 7]
Or another example:
lista = [1,2,['a','b'],[10]]
ListOfLists(lista)
Expected output:
[1, 2, 'a', 'b', 10]
I'm not allowed to use any framework or library.
You'll need a recursive function (a function that calls itself) to walk through any depth of nested lists.
If you're allowed (and know how) to write a generator function,
def flatten_lists(lista):
for item in lista:
if isinstance(item, list):
yield from flatten_lists(item)
else:
yield item
lista = [1, 2, [3, [4, 5, [6]], 7]]
print(list(flatten_lists(lista)))
does the trick quite elegantly.
Without using generators, you'd do
def flatten_lists(lista):
output = []
for item in lista:
if isinstance(item, list):
output.extend(flatten_lists(item))
else:
output.append(item)
return output
To emulate the recursion by hand (i.e. without a real recursive function), you could do
def flatten_lists(lista):
queue = [lista]
output = []
while queue:
item = queue.pop(0)
if isinstance(item, list):
queue = item + queue
else:
output.append(item)
return output
Recursive method
def flatten(list_of_lists):
if len(list_of_lists) == 0:
return list_of_lists
if isinstance(list_of_lists[0], list):
return flatten(list_of_lists[0]) + flatten(list_of_lists[1:])
return list_of_lists[:1] + flatten(list_of_lists[1:])
print(flatten([[1, 2, 3, 4], [5, 6, 7], [8, 9, [10]], 11]))
Related
This question already has answers here:
Flatten an irregular (arbitrarily nested) list of lists
(51 answers)
Closed 6 months ago.
Given a nested list of integers, implement an iterator to flatten it. Each
element is either an integer, or a list -- whose elements may also be integers
or other lists. For example, if the input is [[1,1],2,[1,1]], then the output
is [1, 1, 2, 1, 1]. If the input is [1,[4,[6]]], then the output is [1, 4, 6].
Would anyone be able to advise me as to where the code below went wrong?
I am just starting out with python.
def eb34(list1):
flat_list = []
for i in range(len(list1)):
if type(list[i]) == list:
flat_list += flatten(list1[i])
else:
flat_list.append(list1[i])
return flat_list
You can use recursion:
def flatten(arg):
if not isinstance(arg, list): # if not list
return [arg]
return [x for sub in arg for x in flatten(sub)] # recurse and collect
print(flatten([[1,1],2,[1,1]])) # [1, 1, 2, 1, 1]
print(flatten([1,[4,[6]]])) # [1, 4, 6]
Or to make a generator,
def flatten(arg):
if not isinstance(arg, list): # if not list
yield arg
else:
for sub in arg:
yield from flatten(sub)
print(*flatten([[1,1],2,[1,1]])) # 1 1 2 1 1
print(*flatten([1,[4,[6]]])) # 1 4 6
I don't know from where you are calling flatten() in your code. I am giving you a solution with the other information you have given.
def eb34(list1):
flat_list = []
for i in list1:
if isinstance(i, list):
for j in eb34(i):
flat_list.append(j)
else:
flat_list.append(i)
return flat_list
You can recursively flatten it:
def flatten_recursively(lst_in):
lst_out = []
for i in lst_in:
if type(i) == list:
for j in flatten_recursively(i):
lst_out.append(j)
else:
lst_out.append(i)
return lst_out
Also check out this answer, although you might have to adjust it to Python 3+: https://stackoverflow.com/a/10824420/18189622
The easiest way to make a flat list from nested lists is to use the itertools module. The itertools module has a function called chain that takes a list of lists and returns a single list.
>>> import itertools
>>> nested_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> flat_list = list(itertools.chain(*nested_lists))
>>> flat_list
[1, 2, 3, 4, 5, 6, 7, 8, 9]
One of the questions I had on an exam was to write a python program only using recursion (no loops were allowed).
The goal was to convert a list which contained both integers and lists (which also only contained integers) and make a single list only containing these integers.
Everything works fine in the code below until it encounters a list: after this it just stops.
The fix has to be fairly simple but I just can't find it.
a = [1,5,2,[3,4],6]
def list_in_list(l, i = 0):
if i >= len(l) - 1:
return [l[i]] if type(l[i]) == int else list_in_list(l[i], i=0)
elif type(l[i]) == list:
return list_in_list(l[i],i=0)
return [l[i]] + list_in_list(l, i+1)
print(list_in_list(a))
This works for any level of any level of nested lists:
a = [1,5,2,[3, [4, 7]],6]
def flatten(lst):
if not lst:
return []
first, rest = lst[0], lst[1:]
if isinstance(first, list):
return flatten(first) + flatten(rest)
else:
return [first] + flatten(rest)
print(flatten(a))
Prints:
[1, 5, 2, 3, 4, 7, 6]
The other answer did it without a loop as your question asked but I just wanted to make a few points.
1) Don't using type such as type(a) == str. Use isinstance(a, str). If you want to know why see What are the differences between type() and isinstance()?
2) I made the comment above and I know this is from exam, but this type of problem is inefficient to do with just pure recursion. Recursion is useful for a problem like this (I have done stuff like this a lot with JSON) but using pure recursion without using a loop takes up excessive memory and more time:
a = [1,5,2,[3, [4, 7]],6]
def list_in_list(l):
result = []
for element in l:
if isinstance(element, list):
result += list_in_list(element)
else:
result.append(element)
return result
print(list_in_list(a)) # [1, 5, 2, 3, 4, 7, 6]
Since you specified the [python-3x] tag, let's take advantage of some Python 3 features. Also, let's make the flatten() function have a single point of return instead of three:
example = [[8, 3, [2, 4, 1, [9, [7, 5, 6, [0]]]]]]
def flatten(array):
if array:
first, *array = array
if array:
array = flatten(array)
array = ([first] if isinstance(first, int) else flatten(first)) + array
return array
print(flatten(example))
We also save some recursions by doing an empty list check on the remainder of the list to decide if it's worth recuring.
USAGE
> python3 test.py
[8, 3, 2, 4, 1, 9, 7, 5, 6, 0]
>
The aa list is my input, and the bb list my desired output. The operation is rather simple (change every element in aa to string appending some characters, while keeping the original shape), but the way I do it seems unnecessarily convoluted.
Is there a better (more Pythonic) way to do this?
aa = [0, 5, 6, [8], 3, [9, 2]]
bb = []
for e1 in aa:
if type(e1) == list:
bb2 = []
for e2 in e1:
bb2.append('sss' + str(e2))
bb.append(bb2)
else:
bb.append('sss' + str(e1))
print(bb)
['sss0', 'sss5', 'sss6', ['sss8'], 'sss3', ['sss9', 'sss2']]
You can use a recursive list comprehension to achieve this
def f(data):
return [f(i) if isinstance(i, list) else 'sss{}'.format(i) for i in data]
For example
>>> aa = [0, 5, 6, [8], 3, [9, 2]]
>>> f(aa)
['sss0', 'sss5', 'sss6', ['sss8'], 'sss3', ['sss9', 'sss2']]
You could do what your question title is already hinting at, i.e. use recursion:
def transform(ll):
if isinstance(ll, list):
return list(map(transform, ll))
else:
return 'sss%s' % ll
print(transform([0, 5, 6, [8], 3, [9, 2]]))
Well, one way to do this recursively that is a little more concise is
def addSSS(item):
if isinstance(item, list):
# map takes a function, and a list and returns a
# new list that is the result of applying the function
# to each element. list() converts the result to a list
return list(map(addSSS, item))
# if it's not a list simply append the string and return that value
return "sss" + str(item)
bb = list(map(addSSS, aa))
Maps are nice because they don't evaluate each element right away, only on demand, of course here it gets evaluated right away when you call list() to turn it back to a list
This has the additional advantage that it works when your list nests deeper like aa = [1, 2, [1, 2, [1, 2]]]
I'm learning recursion and I can't quite figure out why this isn't working.
What it should do:
>>> copy(1)
[1, 1]
>>> copy([1, 2])
[1, 1, 2, 2]
>>> copy([1, [2, 3]])
[1, 1, [2, 2, 3, 3]]
So basically the code should just duplicate each integer. Note: The position in the list and the format (if it is a nested list) does not change, all this code does is insert a duplicate int beside each int in the list.
Code:
def copy(nested_list):
new_list = []
#if list is empty
if isinstance(nested_list, list) and len(nested_list) == 0:
return new_list
# if it's only an int
elif isinstance(nested_list, int):
new_list.append(nested_list)
new_list.append(nested_list)
else:
# if list is a list
if isinstance(nested_list, list):
for num in range(len(nested_list)):
if isinstance(nested_list[num], int):
new_list.append(nested_list[num])
new_list.append(nested_list[num])
elif isinstance(nested_list[num], list):
copy(nested_list[num])
else:
pass
return new_list
It works for most of the examples, except the last one.
What it keeps giving back for the last example:
Expected:
[1, 1, [2, 2, 3, 3]]
Got:
[1, 1]
Check this one!
def copy(nested_list):
new_list = []
#if list is empty
if isinstance(nested_list, list) and len(nested_list) == 0:
return new_list
# if it's only an int
elif isinstance(nested_list, int):
new_list.append(nested_list)
new_list.append(nested_list)
else:
# if list is a list
if isinstance(nested_list, list):
for num in range(len(nested_list)):
if isinstance(nested_list[num], int):
new_list.append(nested_list[num])
new_list.append(nested_list[num])
elif isinstance(nested_list[num], list):
tempList = copy(nested_list[num])
new_list.append(tempList)
else:
pass
return new_list
print(copy([1, [2, 3]]))
Your copy function is recursive, but you ignore the result of the recursive call to copy entirely:
elif isinstance(nested_list[num], list):
copy(nested_list[num])
^^^^^^^^^^^^^^^^^^^^^^
You probably want to append the items returned by your call to copy to the end of new_list:
new_list.append(copy(nested_list[num]))
You can also simplify it a little:
def copy(nested_list):
if isinstance(nested_list, int):
return copy([nested_list])
result = []
for elem in nested_list:
if isinstance(elem, int):
result.append(elem)
result.append(elem)
else:
result.append(copy(elem))
return result
Here's another implementation!
def copy(items):
if isinstance(items, int):
return [items, items]
result = []
for element in items:
if isinstance(element, int):
result.extend(copy(element))
else:
result.append(copy(element))
return result
The trick to recursion is to solve the most basic case and call itself to solve a (now) smaller problem:
def repeat_int(seq_or_int, nested=False):
try:
first, *rest = seq_or_int
except TypeError: # got int
return [seq_or_int] * 2 # repeat int
except ValueError: # empty
result = []
else:
result = repeat_int(first, nested=True) + repeat_int(rest)
return [result] if nested else result # nest in a list if necessary
Example:
>>> repeat_int(1)
[1, 1]
>>> repeat_int([1, 2])
[1, 1, 2, 2]
>>> repeat_int([1, [2, 3]])
[1, 1, [2, 2, 3, 3]]
There are two basic cases here:
the input is not an iterable (a scalar such as int)—return the repeated input in a list
the input is empty—return an empty list.
The Python 3 iterable unpacking syntax chops off the first item in the input collection:
first, *rest = seq_or_int
To get the result for the rest of the collection, the recursive call is made with (now) smaller list: repeat_int(rest).
All that is left is to deal with the first item that is the same as the input may be a collection too. To preserve the structure, the nested indicator is passed and if it is set then the result list is wrapped in another list.
I want to implement a definition that will remove all None objects from a list or nested list of lists.
Example:
_list = [0,0,1,None,12, ""]
Return:
_result = [0,0,1,12,""]
Or if the input is a nested list of lists:
Example:
_list = [[1,2,None],[2,3,None,2],[2,3,None,None,""],[None,None,None]]
Return:
_result = [[1,2],[2,3,2],[2,3,""]]
Of course I am not sure what the incoming structure will be (depth of lists) or if some of them will contain all None values (in that case i want to remove the empty list as well).
i did see some examples here but they are all mostly list comprehension or iteration examples and none of them can handle deeper lists. All help will be appreciated.
I am in IronPython 2.7.
Thank you,
This simple recursive function will clean out your nested lists of Nones as well as empty lists (originally or due to removal of Nones):
def clear_list(l):
out = []
for ll in l:
if ll is None:
continue
if isinstance(ll, list):
ll = clear_list(ll)
if not ll:
continue
out.append(ll)
return out
def remove_None(_list):
while None in _list:
_list.remove(None)
for element in _list:
if isinstance(element, list):
remove_None(element)
I hope this may help you!
You could do it with a recursive function that uses a list comprehension:
def clean_list(a_list):
return [obj for obj in [clean_list(obj) if isinstance(obj, list) else obj
for obj in a_list if obj is not None] if obj != []]
_list1 = [0,0,1,None,12, ""]
print(clean_list(_list1))
_list2 = [[1,2,None],[2,3,None,2],[2,3,None,None,""],[None,None,None]]
print(clean_list(_list2))
_list3 = [[1,2,None],[2,3,[4,None,"",5],6],[7,[[[8,[None],"test"],None]]]]
print(clean_list(_list3))
Output:
[0, 0, 1, 12, '']
[[1, 2], [2, 3, 2], [2, 3, '']]
[[1, 2], [2, 3, [4, '', 5], 6], [7, [[[8, 'test']]]]]