Finding index in nested list - python

I am trying to create a function that will take as input a nested list and an item, and return a list of indices.
For example list = [0, 5, [6, 8, [7, 3, 6]], 9, 10] and item = 7 should return [2, 2, 0], since list[2][2][0] = 7
my code should work since I can print the desires output, but when i run it it returns None.
def find_item(list_input, item, current):
if list_input == item:
print(current) ## this does give the desires output, but the function should return not print
return current
else:
if isinstance(list_input, list):
for j in range(len(list_input)):
current.append(j)
find_item(list_input[j], item, current)
del current[-1]
what am I overlooking here?

As #tzaman mentioned, you need to handle the return value of find_item recursive call. If the return value of the recursive call is a list, it will mean that searched item is found and we need to stop the recursion.
The following modification will return the earliest found index of the searched item. If no item is found, it will return None.
def find_item(list_input, item, current):
if list_input == item:
return current
else:
if isinstance(list_input, list):
for j in range(len(list_input)):
current.append(j)
search_result = find_item(list_input[j], item, current)
if isinstance(search_result, list):
return search_result
del current[-1]
list_input = [0, 5, [6, 8, [7, 3, 6]], 9, 10]
item = 7
print(find_item(list_input, item, []))
list_input = [0, 5, [6, 8, [7, 3, 6]], 9, 10]
item = 9
print(find_item(list_input, item, []))
list_input = [0, 5, [6, 8, [7, 3, 6]], [30, 4], 9, 10]
item = 4
print(find_item(list_input, item, []))
list_input = [0, 5, [6, 8, [7, 3, 6]], [30, 4], 9, 10]
item = 400
print(find_item(list_input, item, []))
Output:
[2, 2, 0]
[3]
[3, 1]
None

It always bothers me when someone sticks a for loop in the middle of a recursive function! Here's a different way to go about this problem:
def find_item(list_input, item, index=0):
if list_input:
head, *tail = list_input
if head == item:
return [index]
if isinstance(head, list):
if result := find_item(head, item):
return [index] + result
return find_item(tail, item, index + 1)
return list_input
list_input = [0, 5, [6, 8, [7, 3, 1]], 9, 10]
print(find_item(list_input, 7))
This solution doesn't require an explicit third argument. And returns an empty list upon failure to find the item, rather than None. Note the use of the new walrus operator :=
if result := find_item(head, item):
If that's a problem, instead do:
result = find_item(head, item)
if result:

Related

List index out of range and giving the output ans still

`
list1 = [0,[1, 2, 3], [7, [5, 6]], [7], [8, 9]]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ans=[]
i = 0
n = len(list1)-1
while(n-2):
print(type(list1))
condition = (type(list1[i]) == int)
if condition == True:
ans.append(list1[i])
else:
for j in range(0, len(list1[i])):
condition1 = (type(list1[i][j]) == list)
if condition1 == True:
for k in range(0, len(list1[i][j])-1):
ans.append(list1[i][j][k])
else:
ans.append(list1[i][j])
i+=1
print(ans)
Can anyone help me`
I was trying to simplify the list and i used list for saving it but WHy Index of range. and Still my ans is getting save and giving the right output.
There are 5 items in list1 (see below):
0
[1, 2, 3]
[7, [5, 6]]
[7]
[8, 9]
Your code is incrementing beyond the index of this list
To flatten the list:
list1 = [0,[1, 2, 3], [7, [5, 6]], [7], [8, 9]]
def flatten_list(my_list):
is_not_Flat = True
my_list = [item for sublist in [item if type(item)==list else [item] for item in my_list] for item in sublist]
while is_not_Flat:
for item in my_list:
if type(item) == list:
my_list = flatten_list(my_list)
is_not_Flat = False
return my_list
print(flatten_list(list1))
Output:
[0, 1, 2, 3, 7, 5, 6, 7, 8, 9]

Recursion on nested list

I am trying to use recursion on a nested list. Here I am trying to get the innermost element.
input_list = [1,2,3,4,[5,6,7,[8,9,[10]]]]
list1 = input_list
def sublist1(list, sublist=[]):
if len(list) == 0:
return sublist
else:
sublist.append(list)
sublist1(list[1:], sublist)
print(sublist)
sublist1(list1)
The output I am getting is this:
[[1, 2, 3, 4, [5, 6, 7, [8, 9, [10]]]], [2, 3, 4, [5, 6, 7, [8, 9, [10]]]], [3, 4, [5, 6, 7, [8, 9, [10]]]], [4, [5, 6, 7, [8, 9, [10]]]], [[5, 6, 7, [8, 9, [10]]]]]
I tried changing the index but it's not giving me the expected output [10].
Any help would be appreciated.
You can make a recursive call only if any item in the current given list is a list; otherwise it means the current list is already the innermost list, so you can return the list as is:
def inner_most(lst):
for i in lst:
if isinstance(i, list):
return inner_most(i)
return lst
input_list = [1,2,3,4,[5,6,7,[8,9,[10]]]]
print(inner_most(input_list))
This outputs:
[10]
Demo: https://replit.com/#blhsing/SwelteringFlakyDividend
This will answer your nested travels
input_list = [1,2,3,4,[5,6,7,[8,9,[10]]]]
list1 = input_list
def sublist1(list):
if(len(list) > 1):
for l in list:
#print(l)
# print(type(l))
if type(l) == type([]) :
return sublist1(l)
else:
continue
return list
print(sublist1(list1))
print(list1[-1][-1][-1])
This would get you [10]

How to deal with this exercise using recursion in Python?

I am struggling to solve an exercise regarding lists in Python. The exercise says:
Write a function that takes a list containing numbers and lists of
numbers inside them, reverses it (including the lists of numbers
inside the main list) using recursion.
Afterwards, the function should return a tuple containing three
elements. The first element represents the amount of all the numbers
present in the list (including the ones in the sublists), the second
element is the sum of all the numbers (also the ones inside the
sublists), and the third element is a sorted list of all the integers.
Again we should use recursion.
I managed to reverse the whole list using two functions, but when it comes to accessing the numbers inside the sublists to use them for the tuple, I really don't know what to do.
Here you can have a look at the list of lists:
lista = [3, 3, 5, [[1, 8, [9, 3]], 3, [2, [9, [5, 6],[9]] ] ]]
Here you can check my code:
def exercise (lista):
lista_ordinata = []
count = 0
somma = 0
reverse_list(lista)
for x,y in enumerate(lista):
if isinstance (x,(int)):
count += 1
else:
count = 0
for num in lista:
if isinstance(num,(int)):
somma += num
for i in lista:
if isinstance(i,int):
lista_ordinata.append(i)
return (count,somma,lista_ordinata)
def is_list (lista):
return isinstance(lista,list)
def reverse_list(lista):
lista_nuova = lista[::-1]
for x,y in enumerate(lista_nuova):
if is_list(y):
lista_nuova[x] = reverse_list(y)
lista.clear()
lista.extend(lista_nuova)
return lista
Here you can see the expected list which I reversed:
lista = [[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
The function must return the following tuple:
(13,66, [1,2,3,5,6,8,9])
The output I get is incorrect:
(4, 11, [5, 3, 3])
The first element should be the counting of all the numbers, and not just the numbers outside the sublists, The sum is also incorrect, The list is not outputting all the numbers.
What should I do? Keep in mind that the "exercise" function should use recursion.
You are using recursion just fine in the reverse function, just use recursion in you exercise function:
def exercise(lista):
lista_ordinata = []
count = 0
somma = 0
# reverse_list(lista)
for x in lista:
if isinstance(x, list):
recursion = exercise(x) # call recursion until you have a list with only integers
# and add the result to your running totals
count += recursion[0]
somma += recursion[1]
lista_ordinata.extend(recursion[2])
else:
count += 1
somma += x
lista_ordinata.append(x)
lista_ordinata = sorted(list(set(lista_ordinata)))
return count, somma, lista_ordinata
print(exercise(lista))
(13, 66, [1, 2, 3, 5, 6, 8, 9])
def exercise(a, polish=sorted):
if not isinstance(a, list):
return 1, a, {a}
a.reverse()
amount, sum, numbers = 0, 0, set()
for b in a:
a, s, n = exercise(b, set)
amount += a
sum += s
numbers |= n
return amount, sum, polish(numbers)
Or with a little helper doing the reversals and collecting the numbers:
def exercise(a):
def go(a):
if isinstance(a, list):
a.reverse()
for b in a:
go(b)
else:
numbers.append(a)
numbers = []
go(a)
return len(numbers), sum(numbers), sorted(set(numbers))
Demo:
lista = [3, 3, 5, [[1, 8, [9, 3]], 3, [2, [9, [5, 6],[9]] ] ]]
>>> exercise(lista)
(13, 66, [1, 2, 3, 5, 6, 8, 9])
>>> lista
[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
Here's a way without any loops at all.
First of all, we can have a definition for a partial insertion sort, which inserts any value to its correct position in an existing sorted array.
def partialInsertionSort(val, idx, sortarr):
if idx == len(sortarr):
sortarr.append(val)
elif val < sortarr[idx]:
sortarr.insert(idx, val)
elif val > sortarr[idx]:
sortarr = partialInsertionSort(val, idx+1, sortarr)
return sortarr
As you travel through any list a, unless you've reached the end of a, there are two possibilities:
The current item is another list -> Recurse through the sublist!
The current item is a digit -> Increment total and count. If this digit is a previously unseen digit, 'insert' it to your list of sorted digits.
Done with the above two possible options, you have thus processed the current element, and can move to the next element by incrementing the index i.
def reverse_and_sort(a, i, out, sortarr, total, count):
if i==len(a):
return out, sortarr, total, count
if isinstance(a[i], list):
a[i], sortarr, total, count = reverse_and_sort(a[i], 0, [], sortarr, total, count)
else:
total += a[i]
count += 1
if a[i] not in sortarr:
sortarr = partialInsertionSort(a[i], 0, sortarr)
out.insert(0, a[i])
return reverse_and_sort(a, i+1, out, sortarr, total, count)
Test:
rev, srt, total, count = reverse_and_sort(lista, 0, [], [], 0, 0)
print(rev) #[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
print(srt) #[1, 2, 3, 5, 6, 8, 9]
print(total) #66
print(count) #13
A relatively short version, which uses the add operator is given by that:
from operator import add
def sorter(l):
if isinstance(l, list):
temp = [0, 0, []]
for e in map(sorter, l):
temp = list(map(add, e, temp))
return temp[0], temp[1], sorted(set(temp[2]))
return 1, l, [l]
It should give the correct result:
test_list = [[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
sorter(test_list)
>>> (13, 66, [1, 2, 3, 5, 6, 8, 9])
Two liner approach!
Here is a simple solution to the problem, using indirect recursion. Indirect recursion is where a function f(x) calls a function g(x) which in turn calls f(x).
If your problem was just the first part, the solution is just a simple 2 liner -
f = lambda x: [g(i) for i in reversed(x)]
g = lambda x: f(x) if type(x)==list else x
f(lista)
[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
Beautiful right?
The main idea here is that at an element level you just need to check if the element is an integer and return it, else you need to use another function that lets you iterate over the list. You can have counters at the level of the second function which lets you track the sum and the count and return it with the final function call.
Since you also have the 2nd part to the solution which is using a few counters, you can modify the above code for g(x) to incorporate that as below -
cnt = []
sm = []
#function to iterate over a reversed list
f = lambda x: [g(i) for i in reversed(x)]
#function to call f if list else updated counters and return element
def g(x):
if type(x)==list:
return f(x)
else:
cnt.append(1) #ONLY MODIFICATION
sm.append(x) #ONLY MODIFICATION
return x
#call f and return sum of counters
def rec(l):
o = f(l)
return o, (sum(cnt), sum(sm), sorted(sm))
out, tup = rec(lista)
print(out)
print(tup)
[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
(13, 66, [1, 2, 3, 3, 3, 3, 5, 5, 6, 8, 9, 9, 9])
def exercise(lista, total = 0, count = 0, all_int = []):
for item in lista:
if isinstance(item, list):
count, total, all_int = exercise(item, total, count, all_int)
elif isinstance(item, int):
total += item
count += 1
if item not in all_int:
all_int.append(item)
all_int.sort()
lista.reverse()
return count, total, all_int
Maybe this would help :)
def exercise (lista):
reverse_list(lista)
r = count_numbers(lista), sum_numbers(lista), sorted(pull_out(lista))
return r
def is_list (lista):
return isinstance(lista,list)
def reverse_list(lista):
lista_nuova = []
for e in lista[::-1]:
if is_list(e):
lista_nuova.append(reverse_list(e))
else:
lista_nuova.append(e)
return lista_nuova
def count_numbers(lista):
c = 0
for e in lista:
if is_list(e):
c += count_numbers(e)
else:
c+=1
return c
def sum_numbers(lista):
s = 0
for e in lista:
if is_list(e):
s += sum_numbers(e)
else:
s+=e
return s
def pull_out(lista):
b_lista = []
for e in lista:
if is_list(e):
b_lista.extend(pull_out(e))
else:
b_lista.append(e)
return b_lista
lista = [3, 3, 5, [[1, 8, [9, 3]], 3, [2, [9, [5, 6],[9]] ] ]]
r = exercise(lista)
print(r)
# (13, 66, [1, 2, 3, 3, 3, 3, 5, 5, 6, 8, 9, 9, 9])
I did it with separate functions for clarity.

Function won't return value

I have an assignment to code my own map function and I'm not sure why it's not returning any value. Here's the code below:
def mymap(func, lst):
new_lst = []
for items in lst:
new_lst.append(func(items))
return new_lst
mymap(abs, [3,-1, 4, -1, 5, -9])
It should return [3, 1, 4, 1, 5, 9], but when I run it, it doesn't return anything.
You need to add print in:
def mymap(func, lst):
new_lst = []
for items in lst:
new_lst.append(func(items))
return new_lst
print(mymap(abs, [3,-1, 4, -1, 5, -9]))
Outputs:
[3, 1, 4, 1, 5, 9]

Recursive function to split a list in python

I have a function I've written to take a list of arbitrary values and to split it at certain values into sublists. Basically, I want to take a list and split it at all occurrences of a specific value, returning a list of sublists. I figured the easiest way to do this would be via a recursive function as below.
def recsplit(L, val):
if L.count(val)==0: # If there are no occurrences, return the whole list
return L
elif L.index(val)==0: # If the value is the first element, return everything else
return recsplit(L[1:],val)
else: # Otherwise, split at the first instance of value
return L[:L.index(val)], recsplit(L[L.index(val)+1:],val)
The function should work like this:
>>> P = [1,2,3,4,5,None,None,6,7,8,None,9,10,11,None]
>>> recsplit(P,None)
[[1,2,3,4,5],[6,7,8],[9,10,11]]
Unfortunately I get the following output:
([1, 2, 3, 4, 5, 6, 7], ([8, 9, 10, 11], ([12, 13, 14, 15], [])))
I'm sure there's a way to handle this, but I have tried as many combinations I can think of and none seem to work for me.
I don't think recursion is the easiest way to do this, when you can use itertools.groupby:
from itertools import groupby
lst = [list(g) for k, g in groupby(P, lambda x: x is not None) if k]
print(lst)
# [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11]]
Also keep in mind that recursion is not cheap.
As someone already pointed out, recursive function might not be the best way (at least in python) for this specific task. But since you asked, here is the code with recursive call to generate the exact output you expected.
def recsplit(L, val, out=[[]]):
if L == []:
return out
elif L[0] == val and out[-1] != [] and L[1:].count(val) != len(L[1:]):
return recsplit(L[1:], val, out + [[]])
elif L[0] != val and L[0] is not None:
return recsplit(L[1:], val, out[:-1] + [out[-1] + [L[0]]])
else:
return recsplit(L[1:], val, out)
P = [1,2,3,4,5,None,None,6,7,8,None,9,10,11,None]
P1 = [1,2,None,3,4,5,None,"x","x",None,6,7,8,"x",9,10,11,None,"x","x"]
print("Subs of P by None =", recsplit(P,None))
print("Subs of P1 by x =", recsplit(P1,"x"))
print("Subs of P1 by None =", recsplit(P1,None))
==>
Subs of P by None = [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11]]
Subs of P1 by x = [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11]]
Subs of P1 by None = [[1, 2], [3, 4, 5], ['x', 'x'], [6, 7, 8, 'x', 9, 10, 11], ['x', 'x']]

Categories