How would you write a simple version of copy.deepcopy()? - python

For nested lists, what would the pseudocode look like so that
mylist = [[1,2],[3,[4,5]],6]
def mydeepcopy(mylist)
>> [[1,2],[3,[4,5]],6]
where it returns a completely different list through all layers
here is all I can think of
def mycopy(mylist):
if mylist is not 1-dimensional:
mycopy(elem for elem in mylist)
else:
add mylist.copy() to new list

You have to collect the results of the recursion and return them.
def mycopy(mylist):
newlist = []
for element in mylist:
if isinstance(element, list):
newlist.append(mycopy(element))
else:
newlist.append(element)
return newlist
This is overly simplified, as it only deals with lists, not tuples and dictionaries.
Note that "is not 1-dimensional" is not something you test for the entire list. It can contain a mixture of nested lists and other objects, so you need to test each element in the loop.

Related

Recursion removing list elements Python

need to write a recurive function to remove elements of list but keep the arrays in the result such as.
def remove_words(listx):
for element in listx:
if isinstance(element, list):
remove_words(element)
else:
listx.remove(element)
return listx
remove_words(["a", "b", ["c"]]) would return [[]]
My code returns ["b",[]] for some reason it's missing an element.
Do not remove elements from a collection while iterating it. Repeated calls to list.remove are also not ideal performance-wise since each removal (from a random index) is O(N). A simple way around both issues would be the following comprehension:
def remove_words(listx):
return [remove_words(e) for e in listx if isinstance(e, list)]
The seemingly missing base case is a list with no nested lists where an empty list is returned.
If you want to modify the list in-place, you can use slice assignment:
def remove_words(listx):
listx[:] = [remove_words(e) for e in listx if isinstance(e, list)]
def remove_words(listx):
if listx == []:
return []
elif not isinstance(listx[0], list):
return remove_words(listx[1:])
else:
return [remove_words(listx[0])] + remove_words(listx[1:])
print(remove_words(["a", "b", ["c"]]))
print(remove_words(["a", ["b",["c","d"],"c"], [["f"],["g"]],"h"]))

Any element in list is an integer

I have a list, which potentially contains a list (and this subsequently can also can be made up of lists, ad infinitum). Is there a way to test if any element at the 'bottom' of these lists has an integer? The list will always have numbers (floats or ints) eventually.
As I don't know the number of nested lists of it beforehand, the only way I can think of doing so would be something like,
x = [[[[5]]]]
if (len(str(x)) != len(str(x).replace('.','')) or ('int' not in str(x)):
int_in_list = False
Is there a more logical way of doing this?
I'd recommend using something like collapse() from the more-itertools library, which will recursively descend into lists and other iterable types and yield the fundamental non-iterable elements. You can then just check whether any of the elements yielded by collapse() is an integer.
any(isinstance(x, int) for x in collapse(...))
Here ya go:
numlist = [[[[[1]]]]]
def recursive_flatten(lst):
for item in lst:
if isinstance(item, list):
yield from recursive_flatten(item)
yield item
if any(isinstance(item, int) for item in recursive_flatten(numlist)):
# Horray
pass

Exclude items from list of lists Python

I have the next list of
testList = []
testList.append([0,-10])
testList.append([-12,122])
testList.append([13,172])
testList.append([17,296])
testList.append([-10,80])
testList.append([-16,230])
testList.append([-18, 296])
testList.append([-2, -8])
testList.append([-5,10])
testList.append([2,-4])
and another lists which contains elements from previous list:
m1 = []
m1.append([0, -10])
m1.append([13, 172])
Then I try to get a subarray from the list testList with the next statement:
[element for i, element in enumerate(testList) if i not in m1]
But I get the same list as testList.
How can I achieve this?
If you don't care about the order in the lists, you can use sets instead:
# result will be a set, not a list
not_in_testlist = set(testlist) - set(m1)
If you want the result to be a list again:
# result will be a list with a new order
not_in_m1 = list(set(testlist) - set(m1))
Be aware that using sets will lose the order of the original lists because sets are unordered types (they use hashing under the hood).
If you need to preserve the order, then Andrew Allaire's answer is correct:
# result is a list, order is preserved
not_in_testlist = [e for e in testlist if e not in m1]
The problem is with your use of enumerate. The i is just going to be an integer, and therefor never in a list that only has lists in it. Try this:
[element for element in testList if element not in m1]
Try with this:
def clean_list(my_list, exclusion_list):
new_list = []
for i in my_list:
if i in exclusion_list:
continue
else:
new_list.append(i)
return new_list

Why does removing duplicates from a list produce [None, None] output?

I am new to Python and I'm not able to understand why I am getting the results with None values.
#Remove duplicate items from a list
def remove_duplicates(list):
unique_list = []
return [unique_list.append(item) for item in list if item not in unique_list]
print remove_duplicates([1,1,2,2]) -> result [None, None]
When I print the result it shows the following: [None, None]
PS: I've seen other solutions and also aware of the list(set(list)) but I am trying to understand why the above result with integers gives [None, None] output.
Although using a set is the proper way, the problem with your code, as the comments indicated, is that you are not actually returning unique_list from your function, you are returning the result of the list comprehension.
def remove_duplicates(my_list):
unique_list = []
do = [unique_list.append(item) for item in my_list if item not in unique_list]
return unique_list # Actually return the list!
print remove_duplicates([1,1,2,2]) -> result [1, 2]
Here I simply made a throwaway variable do that is useless, it just "runs" the comprehension. Understand?
That comprehension is storing a value each time you call unique_list.append(item) ... and that value is the result of the append method, which is None! So do equals [None, None].
However, your unique_list is in fact being populated correctly, so we can return that and now your function works as expected.
Of course, this is not a normal use for a list comprehension and really weird.
The problem with your code is that the method list.append returns None. You can test this easily with the following code:
myList=[1, 2, 3]
print myList.append(4)
So, a solution for you would issue would be
def remove_duplicates(myList):
alreadyIncluded = []
return [item for item in myList if item not in alreadyIncluded and not alreadyIncluded.append(item)]
print remove_duplicates([1,1,2,2])
The idea is that you will begin with an empty list of aldeady included elements and you will loop over all the elements in list, including them in the alreadyIncluded list. The not is necessary because the append will return None and not None is True, so the if will not be affected by the inclusion.
You were including a list of the result of the appends (always None), but what you need is a list of the elements that passed the if test.
I hope it helps.
As the other answers have explained, the reason you're getting a list of None values is because list.append returns None, and you're calling it in a list comprehension. That means you're building a list full of None values along side your list of unique values.
I would like to suggest that you ditch the list comprehension. Because you need to access outside state (the list of unique values seen so far), a comprehension can't easily do what you want. A regular for loop is much more appropriate:
def remove_duplicates(lst):
unique_list = []
for item in lst:
if item not in unique_list:
unique_list.append(item)
return unique_list
A more Pythonic approach however would be to use a set to handle the unique items, and to make your function a generator:
def remove_duplicates(lst):
uniques = set()
for item in lst:
if item not in unique_list:
yield item
uniques.add(item)
The itertools.ifilterfase function from the standard library can help improve this even further, as shown in the recipe in the docs (you'll have to scroll down a little to find the specific recipe):
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element

Search a nested list against items from alternative list, returning subsets that contain partial match

Say I have two lists such as:
NestedLst = ['1234567', 'abc456789', ['cde678945']]
SearchLst = ['123', 'cde']
How do I search the nested list for any partial matches with any of the items of the search list and return for each search item the subset list. So in this case I want the output to be:
['1234567', 'abc456789', ['cde678945']] and ['cde678945']
I have tried doing the below:
indices = []
for eachItem in SearchLst:
for i, elem in enumerate(NestedLst):
if eachItem in elem:
indices.append(i)
print indices
but this always returns [0]. Any help would be much appreciated. As I am new to Python a full explanation of the code would be very helpful for me to learn.
Thankyou
Below is an example nested list in practice:
[['BMNH833953:0.16529463651919140688', [[['BMNH833883:0.22945757727367316336', ['BMNH724182a:0.18028180766761139897', ['BMNH724182b:0.21469677818346077913', 'BMNH724082:0.54350916483644962085'], ':0.00654573856803835914'], ':0.04530853441176059537'], ':0.02416511342888815264', [[['BMNH794142:0.21236619242575086042', ['BMNH743008:0.13421900772403019819', 'BMNH724591:0.14957653992840658219'], ':0.02592135486124686958'], ':0.02477670174791116522', 'BMNH703458a:0.22983459269245612444'], ':0.00000328449424529074', 'BMNH703458b:0.29776257618061197086'], ':0.09881729077887969892'], ':0.02257522897558370684', 'BMNH833928:0.21599133163597591945'], ':0.02365043128986757739', 'BMNH724053:0.16069861523756587274'], ':0.0;\n']
You can do that using a method and recursion:
NestedLst = ['1234567', 'abc456789', ['cde678945']]
SearchLst = ['123', 'cde']
def get_list(a,b):
for i in b:
if type(i)==list: #if this is a nested list, do the same function for the contents of that list, returning that list if it meets the conditions.
return get_list(a,i)
elif a in i: #if element in list is not a nested list, just do normal check for containment using in
return b
return []
x = [get_list(i,NestedLst) for i in SearchLst] #just calls the function for all search terms
for k in x:
print k
[OUTPUT]
['1234567', 'abc456789', ['cde678945']]
['cde678945']

Categories