Generate a while loop [duplicate] - python

This question already has answers here:
Flatten an irregular (arbitrarily nested) list of lists
(51 answers)
Closed 2 days ago.
I'm trying to create a while loop for my code. The idea is not to use so many "for" loops to put it into a function
int_list = [[[1]],2,[[[[3]]]]]
for i in int_list:
if type(i) != list:
list_of_lists.append(i)
else:
for a in i:
if type(a) != list:
list_of_lists.append(a)
else:
for j in a:
if type(j) != list:
list_of_lists.append(j)
else:
for h in j:
if type(h) != list:
list_of_lists.append(h)
else:
for r in h:
if type(r) != list:
list_of_lists.append(r)
Expected Output
list_of_lists = [1,2,3]

One way to reduce for loop is to create a recursive function.
The function recursively calls itself on that sublist(i) by passing it as an argument to helper(). The resulting list is then extended to the result list using the extend() method.
If it's instance is not a list. it runs else part. appending value into the lis
Code:
def helper(lis):
res=[]
for i in lis:
if isinstance(i, list):
res.extend(helper(i))
else:
res.append(i)
return res
int_list=[[[1]], 2, [[[[3]]]]]
list_of_lists=helper(int_list)
print(list_of_lists)
Output:
[1,2,3]

A recursive function is the ideal way to flatten a list like this. If you really want to do it in a single while loop, though, it is possible, by creating a stack to help you keep track of where you are in each sublist.
This happens automatically for you when you define and call a recursive function; every call to the function puts all the function's local variables onto a "stack", just like the code below puts the current list and index onto a list called stack every time a new sublist is discovered.
int_list = [[[1]],2,[[[[3]]]]]
flat_list = []
stack = []
i = 0
while True:
if i >= len(int_list):
# We're at the end of this sublist,
# so go back to the stack to pick up where we left off.
if not stack:
break # all done!
int_list, i = stack.pop()
elif isinstance(int_list[i], list):
# Keep track of the current list and the next item
# for us to look at, and then start looking at this sublist.
stack.append((int_list, i+1))
int_list, i = int_list[i], 0
else:
# We found a scalar item! Add it to the flat list.
flat_list.append(int_list[i])
i += 1
assert flat_list == [1, 2, 3]

Related

How to get position of an element from a nested list in python [duplicate]

This question already has answers here:
Find the index of an item in a list of lists
(6 answers)
Closed 2 years ago.
If I have a list that looks like this
list = [[1,2,3], [4,5,'X'], [7,8,'X']]
How can I get the position of X i.e like (i,j)?
list is a python keyword so it is probably better to use some other name. To access an element of list you can use this syntax: listname[indexNumber]. Note that index starts from 0. So, to access the X in the second list in the nested loop, you need to access the second list first; like this secondList = list[1]. Then you can get X which is in the second index position. x = secondList[2]. You can do this in the same line as well: print(list[1][2])
You can iterate over the "outer" list and use index on the inner list:
def find(matrix, item):
for i in range(len(matrix)):
try:
j = matrix[i].index(item)
return (i, j)
except ValueError:
pass
raise ValueError # If item isn't found
Try this
list1 = [[1,2,3], [4,5,'X'], [7,8,'X']]
print(list1[1][2])
print(list1[2][2])
If you don't know where 'X' will be every time:
test_list = [[1,2,3], [4,5,'X'], [7,8,'X']]
x = None
y = None
for separate_list in test_list:
for var in separate_list:
if var == 'X':
if x == None:
x = var
elif y == None:
y = var
pos = (x,y)
print(pos)
You can do this with list slicing.
l = [[1,2,3], [4,5,'X'], [7,8,'X']]
print(l[0][0], l[1][2], l[2][0])
print(l[1][2])
# [0][0] == first list in list and first item in the first list
# [1][2] == second list and third item in the second list
# [2][0] == third list and first item in that list
ouput
1 X 7
X
great python list slicing tuto here

Python - Removing first two occurrences of element in list

The objective of this function is to remove the first two occurrences of n in a list.
Below is a code I had written but I still got it wrong after many hours. A friend advised me not to edit a list while iterating. However, I'm still stuck.
def remove_first_two(list,n):
if list == []:
return []
else:
count = 0
for ele in list:
if ele == n:
list.remove(ele)
count += 1
if count == 2:
break
return list
list = [1,2,2,3]
print(remove_first_two(list,2)) => [1,2,3] instead of [1,3]
Use list.remove twice with try-except. That will delete first two entries. Complexity O(n)
list_a = [1,2,3,4]
try:
list_a.remove(n)
list_a.remove(n)
# run a loop too, if it's more than 2
except:
pass
You can try find all indexes and del:
a = [1,2,3,2,3,2,4]
indices = [i for i, x in enumerate(a) if x == 2]
print(indices)
[1, 3, 5]
del a[indices[0]], a[indices[1]]
print(a)
[1, 3, 2, 2, 4]
First, don't use 'list' as its a key word in Python. Use something else, like 'alist'.
The code below does what you want and keeps the basic form of what you already have. You can of course also use the built-in .remove() method.
def remove_first_two(alist, n):
if alist == []:
return []
else:
count = 0
while count < 2:
for ele in alist:
if ele == n:
alist.remove(ele)
count += 1
return alist
alist = [1,2,2,3]
print(remove_first_two(alist,2)) # Output -> [1,3]
When your friend says "do not edit a list while iterating," he/she is right, and what he/she means is that you should create another list all together. What you are looking to do is the following:
def remove_first_two(list, n):
if list == []:
return []
else:
new_list = []
count = 0
for ele in list:
if ele == n:
if count >= 2:
new_list.append(ele)
count += 1
else:
new_list.append(ele)
return new_list
However, note that you can use use some built in functions to make your life much easier:
list.remove(x)
Remove the first item from the list whose value is equal to x. It raises a ValueError if there is no such item.
Therefore, you can more simply do:
def remove_first_two(list, n):
if list == []:
return []
for _ in range(2):
if n in list:
list.remove(n)
return list
Python updates the list if you change it while iterating.
In you test case with list = [1,2,2,3] when list[1] is deleted and Python updates list = [1,2,3]. Now Python understands you have iterated till index 1 and continues from index 2 which now contains 3. So Python encounters only one occurance of 2.
So heed your friends advice and do not edit list while iterating :)
Now you can use Python's in-built list.remove(element) to delete first ocuurence of a element. Repeat it 2 times for desired output.
Also O(n) with a single parse.
def remove_first_two(mylist,n):
counter = 0
def myfilter (i):
nonlocal counter,n
if counter > 2:
return True
else:
counter += 1
return (i != n)
return (list(filter(myfilter,mylist)))
This can also be done in python 3.8 using assignment expressions in a list comprehension:
data = [1,2,3,2,3,2,4]
count = 2
num = 2
[x for x in data if x != num or (count:=count-1) < 0]
Results:
[1, 3, 2, 2, 4]
Here is the reason why your program does not work:
When you remove an element, the for loop moves on to the next element, but by "moving on" it is actually skipping the element which now occupies the position of the deleted element. It skips the element right after the one you deleted.
The correct way to iterate over a list while you delete elements is making index progression explicit, by using a while loop instead of a for loop, and not increase the index when you delete an element:
i = 0
while i < len(my_list):
if condition:
my_list.pop(i)
else:
i += 1
However, none of this is necessary in your case! Notice that when you use my_list.remove(ele), you are not providing an index as you would with my_list.pop(i), so Python has to search for the first element that matches ele. Although remove will be slower than pop when used by themselves, here remove allows you not use any loops at all, simply do my_list.remove(n) twice!
Last touch: If your list has less than two elements matching n, one of the two my_list.remove(n) commands would return a ValueError. You can account for this exception, knowing that if it happens, your list is ready and requires no further action.
So the code you need is:
try:
my_list.remove(n)
my_list.remove(n)
except ValueError:
pass

How to shorten a code to make multiple lists?

I'm making 12 lists that undergo the same process. Unfortunately, I can't figure out how to make them all do this in a loop, and I've been forced to repeat the same code 12 times and take up a giant chunk of text.
Here's a small portion of the code I've had to write.
l1 = []
l1.append(random.choice(easy))
if "none" in l1:
l1.remove("none")
else:
psblnsrs.append(l1[0])
easy.remove(l1[0])
l1.append(random.choice(special))
if "none" in l1:
l1.remove("none")
elif len(l1) >1:
usblhks.append(l1[1])
else:
usblhks.append(l1[0])
while sum(len(l1) for l1 in l1) < 12:
l1.append(random.choice(junk))
random.shuffle(l1)
l2 = []
l2.append(random.choice(easy))
if "none" in l2:
l2.remove("none")
else:
psblnsrs.append(l2[0])
easy.remove(l2[0])
l2.append(random.choice(special))
if "none" in l2:
l2.remove("none")
elif len(l2) >1:
usblhks.append(l2[1])
else:
usblhks.append(l2[0])
while sum(len(l2) for l2 in l2) < 12:
l2.append(random.choice(junk))
random.shuffle(l2)
Keep in mind, there are twelve lists that need to be made, this is only two.
I'm not too familiar with coding massive loops and naming variables properly. I want something like this:
for i in range(12):
l(i) = []
l(i).append ...
Is there a way to make this work or a similar way to make this work?
Also, if the code is hard to understand, the source material is here.
Functions might come in handy
def make_list(inp_list1=psblnsrs, inp_list2=usblhks, easy_list=easy, special_list=special, junk_list=junk):
l1 = []
l1.append(random.choice(easy_list))
if "none" in l1:
l1.remove("none")
else:
psblnsrs.append(l1[0])
easy.remove(l1[0])
l1.append(random.choice(special_list))
if "none" in l1:
l1.remove("none")
elif len(l1) >1:
usblhks.append(l1[1])
else:
usblhks.append(l1[0])
while sum(len(l1) for l1 in l1) < 12:
l1.append(random.choice(junk_list))
return random.shuffule(l1)
l = []
for i in range(12):
l.append(make_list())
I think you could probably do it pretty much just like your are doing it.
lists = []
for list in lists:
list = []
list.append(random.choice(easy))
if "none" in list:
list.remove("none")
else:
psblnsrs.append(list[0])
easy.remove(list[0])
list.append(random.choice(special))
if "none" in list:
list.remove("none")
elif len(list) >1:
usblhks.append(list[1])
else:
usblhks.append(list[0])
while sum(len(list) for list in list) < 12:
list.append(random.choice(junk))
random.shuffle(list)
Unless I'm missing something about how your code works upstream, you should just be able to do that, you will end up with a list of lists, which you can reference by their index. (e.g. lists(2))
Consider the following code
#Create a list to contain lists
myLists = []
#Add to our list 12 empty lists
for i in range(12):
myLists.append([])
#Loop over each list and add 12 to each list.
for currentList in myLists:
currentList.append(12)
#Print out the results
print(myLists)
by using for currentList in myLists, we can iterate over each list in our "list of lists" and perform operations on the currentList before moving on to the next one. Your code that therefore perform the same operations on each list in turn.
As khelwood pointed out, a better data structure for your twelve lists is to just store them in another list. That way you can access them with super_list[0], super_list[1], and more importantly you can iterate over them with for sublist in super_list: ... rather than having to refer to them explicitly at all.
That way, you only have to write your logic once. What you're looking to do is define a function:
def list_update(current_list):
'''
List management logic code here.
'''
...
return current_list
Then in the main logic of your code:
for sub_list in super_list:
sub_list = list_update(sub_list)
This way, when you have to make a change to the way you're handling those lists, you don't have to write the change 12 times, you only have to write it once.

How To Tell if An Element In A List Is Itself A List? [Python]

As an exercise, I'm creating a code which recursively sums the elements of a list, including lists within lists.
Sample list: a = [1,2,[3,4],[5,6]]
Sample result: 21
My code is as follows:
a = [1,2,[3,4],[5,6]]
def sum(list):
if len(list[0]) == 1 and len(list) ==1: # case where list is [x]
return list[0]
elif len(list[0]) == 1 and len(list) > 1: # case where list is [x, etc]
return list[0] + sum(list[1:])
elif len(list[0]) > 1 and len(list == 1): # case where list is [[x,etc]]
return sum(list[0])
else: # case where list is [[x, etc], etc]
return sum(list[0]) + sum(list[1:])
print (sum(a))
However, when I try to run this I get the error "object of type 'int' has no length." I was under the impression that list[0] would just have a length of 1 if it's not a list in itself, but obviously, that doesn't work. What should I be doing instead to check whether an element in a list is itself a list?
What should I be doing instead to check whether an element in a list is itself a list?
if isinstance(x, list):
Common Gotchas You shouldn't name your own variables as list because you then overwrite the type list and function list()
Similarly for your function sum(). You wouldn't want to overwrite the sum() function that already handles a list of numbers.
Or another way would be hasattr(lst, "__len__")
from numbers import Number
def recur_sum(lst):
# Return single value immeadiately
if not lst or lst is None:
return 0
if isinstance(lst, Number):
return lst
head = None
tail = False
if hasattr(lst, "__len__"):
_size = len(lst)
if _size <= 0: # case where list is []
head = 0
elif _size >= 1:
if hasattr(lst[0], "__len__"): # case where list is [[x,etc]]
head = recur_sum(lst[0])
else: # case where list is [x]
head = lst[0]
tail = (_size > 1)
if not tail:
return head
else:
return head + recur_sum(lst[1:])
In short, you may use the isinstance function to check if an element is a list or not.
The follwoing code may serve as an example.
a = [1,2,[3,4],[5,6]]
a
Out[3]:
[1, 2, [3, 4], [5, 6]]
isinstance(a,list)
Out[4]:
True
isinstance(a[0],list)
Out[5]:
False
isinstance(a[2],list)
Out[6]:
True
For an item in the list you may change the if condition to if type(item) is list:
a = [1,2,[3,4],[5,6]]
for item in a:
if type(item) is list:
# do_something_with(item)
print(item)
and the output will look like this:
[3,4]
[5,6]
Make sure the reserved word list is not overwritten as a variable!

recursively remove adjacent duplicates in a list

I looked up and found a close example, but the answer found in this link: Remove adjacent duplicate elements from a list won't run the test cases for this problem. So this is all I have so far:
def remove_dups(thelist):
"""Returns: a COPY of thelist with adjacent duplicates removed.
Example: for thelist = [1,2,2,3,3,3,4,5,1,1,1],
the answer is [1,2,3,4,5,1]
Precondition: thelist is a list of ints"""
i = 1
if len(thelist) == 0:
return []
elif len(thelist) == 1:
return thelist
elif thelist[i] == thelist[i-1]:
del thelist[i]
return remove_dups(thelist[i:])
def test_remove_dups():
assert_equals([], remove_dups([]))
assert_equals([3], remove_dups([3,3]))
assert_equals([4], remove_dups([4]))
assert_equals([5], remove_dups([5, 5]))
assert_equals([1,2,3,4,5,1], remove_dups([1,2,2,3,3,3,4,5,1,1,1]))
# test for whether the code is really returning a copy of the original list
mylist = [3]
assert_equals(False, mylist is remove_dups(mylist))
EDIT while I do understand that the accepted answer linked above using itertools.groupby would work, I think it wouldn't teach me what's wrong with my code & and would defeat the purpose of the exercise if I imported grouby from itertools.
from itertools import groupby
def remove_dups(lst):
return [k for k,items in groupby(lst)]
If you really want a recursive solution, I would suggest something like
def remove_dups(lst):
if lst:
firstval = lst[0]
# find lowest index of val != firstval
for index, value in enumerate(lst):
if value != firstval:
return [firstval] + remove_dups(lst[index:])
# no such value found
return [firstval]
else:
# empty list
return []
Your assertion fails, because in
return thelist
you are returning the same list, and not a copy as specified in the comments.
Try:
return thelist[:]
When using recursion with list it is most of the time a problem of returning a sub-list or part of that list. Which makes the termination case testing for an empty list. And then you have the two cases:
The current value is different from the last one we saw so we want to keep it
The current value is the same as the last one we saw so we discard it and keep iterating on the "rest" of the values.
Which translate in this code:
l = [1,2,2,3,3,3,4,5,1,1,1]
def dedup(values, uniq):
# The list of values is empty our work here is done
if not values:
return uniq
# We add a value in 'uniq' for two reasons:
# 1/ it is empty and we need to start somewhere
# 2/ it is different from the last value that was added
if not uniq or values[0] != uniq[-1]:
uniq.append(values.pop(0))
return dedup(values, uniq)
# We just added the exact same value so we remove it from 'values' and
# move to the next iteration
return dedup(values[1:], uniq)
print dedup(l, []) # output: [1, 2, 3, 4, 5, 1]
problem is with your return statement,
you are returning
return remove_dups(thelist[i:])
output will be always last n single element of list
like for above soon,
print remove_dups([1,2,2,3,3,3,4,5,1,1,1])
>>> [1] #as your desired is [1,2,3,4,5,1]
which returns finally a list of single element as it don't consider Oth element.
here is recursive solution.
def remove_dups(lst):
if len(lst)>1:
if lst[0] != lst[1]:
return [lst[0]] + remove_dups(lst[1:])
del lst[1]
return remove_dups(lst)
else:
return lst

Categories

Resources