I have defined a recursive function delete() that takes in two parameters:
lst : of type list
to_delete : a value that needs to be removed from the list
However, I have used the function del(), one that we haven't learned yet. So I am wondering whether there is a simpler way to output the same solution without the use of the function del()
This is my code:
def delete(lst, to_delete):
"""
parameters : lst of type list
to_delete : represents a value one wants to delete from the list
returns : another list with the same elements minus the ones one asks to delete
"""
if not lst:
return []
else:
if lst[0] == to_delete:
del lst[0]
return delete(lst[1:], to_delete)
return [lst[0]] + delete(lst[1:], to_delete)
print(delete([1,2,3,4,5,5,6,5,7,5], 5))
Output:
> [1,2,3,4,6] #where is 7 ?
7 is missing, because you return delete(lst[1:], to_delete) even when you just deleted lst[0]: You should use delete(lst[0:], to_delete) here.
The alternative is not to del the 0th element and just return delete(lst[1:], to_delete).
Instead of doing this recursively, you can also just use a list comprehension:
.
def delete(lst, to_delete):
return [element for element in lst if element != to_delete]
Using Recursion
Even with recursion, you don't need to use del:
def delete(lst, to_delete):
"""
parameters : lst of type list
to_delete : represents a value one wants to delete from the list
returns : another list with the same elements minus the ones one asks to delete
"""
if not lst:
return []
if lst[0] == to_delete:
return delete(lst[1:], to_delete)
return [lst[0]] + delete(lst[1:], to_delete)
As you can see, you are repeating yourself a bit (delete(lst[1:], to_delete) is used twice), so you can shorten this to:
def delete(lst, to_delete):
"""
parameters : lst of type list
to_delete : represents a value one wants to delete from the list
returns : another list with the same elements minus the ones one asks to delete
"""
if not lst:
return []
start = [] if lst[0] == to_delete else [lst[0]]
return start + delete(lst[1:], to_delete)
I don't know about its performance though.
No Recursion
If you don't need to use recursion, you can use list comprehensions for much less code:
def delete(lst, to_delete):
return [x for x in lst if x != to_delete]
In case you don't know list comprehensions well, this is logically equivalent to the following:
def delete(lst, to_delete):
res = []
for x in lst:
if x != to_delete:
res.append(x)
return res
EDIT: I missed it, but the reason you don't see 7 in the output is, that del lst[0] already removes the first value from the list and so, you are missing the "new" first value of the list.
You seem to be interested in recursion. Recursion is a functional heritage and so I will give you a glimpse of a functional perspective on the problem. Below, delete is a specialization of filter, which is a specialization of reduce, a simple recursive form -
def reduce (f, state = None, xs = [], i = 0):
if i >= len (xs):
return state
else:
return reduce \
( f
, f (state, xs[i], i)
, xs
, i + 1
)
def filter (f, xs = []):
return reduce \
( lambda acc, x, i:
acc + [x] if f (x) else acc
, []
, xs
)
def delete (q, xs = []):
return filter \
( lambda x: q != x
, xs
)
print (delete (5, [ 1, 2, 5, 3, 5, 5, 2, 3, 1, 5, 1 ]))
# [1, 2, 3, 2, 3, 1, 1]
print (delete ('x', 'abxcdxefxghxi'))
# ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
Functional style is not the idiomatic style of Python. Those wishing to explore functional style in Python are not completely ignored however. You can find reduce and filter (among many other useful functions) tucked away in Python's functools module.
The definitions of reduce and filter here are my own. If you use functools, you'll want to read up on specific behaviors more closely.
It would be much simpler to recreate your list without the unwanted items:
def delete(lst, to_delete):
return [x for x in lst if x!=to_delete]
print(delete([1,2,3,4,5,5,6,5,7,5], 5))
# [1,2,3,4,6,7]
Correcting your code (but leaving recursion out of it) would look like this:
def delete(lst, to_delete):
"""
parameters : lst of type list
to_delete : represents a value one wants to delete from the list
returns : another list with the same elements minus the ones one asks to delete
"""
if not lst:
return []
else:
res = []
for item in lst:
if item == to_delete:
continue
else:
res.append(item)
return res
which has the same result.
Finally, the recursive option which I highly discourage for this application is the following:
def delete(lst, to_delete, res=[]):
"""
parameters : lst of type list
to_delete : represents a value one wants to delete from the list
returns : another list with the same elements minus the ones one asks to delete
"""
if not lst:
return res
else:
item = lst[0]
if item != to_delete:
res.append(item)
return delete(lst[1:], to_delete, res=res)
Related
I think I'm close with this function but I'm having trouble. I need to return the index of the list in a list of lists that contains a particular string. For example, if the list of lists was [['.','c','e','g'],['d',h','t','f']] and my goal was to look for 'c' then the function would need to return 0 since it is in the first list. Or if the goal was to look for 'h' it would return 1 since it is in the second list.
Here is the code I have so far:
def which_one(lst):
for row in lst:
for col in lst:
if col == 'c':
return col
else:
continue
Consider a different approach that avoids using two loops. Making use of enumerate on your list will only require you to iterate once, where each iteration you will have two values, each representing a running count representing your index, and the next your value. You simply check the character you are interested against the value using in, and return the index. Observe the example:
def find_index(l, c):
for i, v in enumerate(l):
if c in v:
return i
res = find_index([['.','c','e','g'],['d', 'h','t','f']], 'c')
print(res) # 0
res = find_index([['.','c','e','g'],['d', 'h','t','f']], 't')
print(res) # 1
Just for the sake of adding it, if you wanted to collect all indexes for the character you are looking up, this can be done in a comprehension:
def all_indexes(l, c):
return [i for i, v in enumerate(l) if c in v]
Demo:
res = all_indexes([['.','c','e','g'],['d', 'h','t','f', 'c']], 'c')
print(res) # [0, 1]
I want to write a function that will remove a certain integer from a list. Both of the values are input. For example, removeValues([1, 2, 3], 3) would return the list [1, 2]:
def removeValues(aList, n):
newList = []
if n in aList:
aList.remove(n)
newList.append(aList)
else:
return False
I am not sure if .remove is the right way to go.
Use a list comprehension
def removeValues(aList, n):
return [ i for i in aList if i != n ]
list(filter(lambda x : x != 3, [1,2,3]))
Use filter, Filter takes a function reference and list of elements. the function is written to accept a list element and return true or false based on the desired predicate like x != 3. for every element the function checks the predicate and only if the condition return True the element is included in the output list.
As said in the comments, a simple remove on the list will do the job.
A function that will remove all occurrences of a specific value from a list could be written like so:
>>> def removeAll(list, value):
... while value in list:
... list.remove(value)
...
>>> a = [1,2,3,3,4]
>>> removeAll(a, 3)
>>> print( a )
[1,2,4]
I am trying to write a recursive function that will perform some action on every object within the list and then output a new list that matches the structure of the processed list exactly. I had some help from users here, but in my example i tried strings which happen to be iterable and work well with map(). However when I am applying it to other data types like int that are not iterable, i get an error:
def process_list(_list):
new_list = []
for x in _list:
if is_list(x):
new_list.append(process_list(x))
else:
new_list.append(map(lambda y: y + 1, x))
return new_list
def is_list(l):
return type(l) == types.ListType
_list = [[0,1,2],[0,1,2],[0,1,2]],[0,1,2]]
i am using Integers as an example here because they are not iterable and i am expecting that map() will fail on a list of integers. Imagine any other data type that is not iterable. Is there another way to define this that will let me perform some function(s) on objects within the list and then create an output list that matches the input list?
thank you,
You are trying to iterate (by using the map() function) over x, which is not iterable in most cases. The reason you can do it for strings is because they are iterable objects in themselves, which is why you didn't run into this right away. You really don't need to use map() at all in your for loop... you could just do
for x in _list:
if is_list(x):
new_list.append(process_list(x))
else:
new_list.append(x+1)
Really the best thing you could do is use map directly:
def process_list(_list):
return map( lambda x: process_list(x) if type(x)==list else x+1, _list )
Try modifying the else clause so it performs simple addition on the item. No map or lambda required.
import types
def process_list(_list):
new_list = []
for x in _list:
if is_list(x):
new_list.append(process_list(x))
else:
new_list.append(x+1)
return new_list
def is_list(l):
return type(l) == types.ListType
_list = [[[0,1,2],[0,1,2],[0,1,2]],[0,1,2]]
print process_list(_list)
Result:
[[[1, 2, 3], [1, 2, 3], [1, 2, 3]], [1, 2, 3]]
If you want calls to process_list to look more like map, you can require an additional parameter, which indicates what kind of processing should be done.
import types
def process_list(func, _list):
new_list = []
for x in _list:
if is_list(x):
new_list.append(process_list(func, x))
else:
new_list.append(func(x))
return new_list
def is_list(l):
return type(l) == types.ListType
_list = [[[0,1,2],[0,1,2],[0,1,2]],[0,1,2]]
print process_list(lambda x: x+1, _list)
check with isinstance to see what the type is and base your logic on that:
def process_list(_list):
new_list = []
for x in _list:
if isinstance(x, list):
new_list.append(process_list(x))
elif isinstance(x, (int, float)):
new_list.append(x+1)
else:
new_list.append(map(lambda y: y + 1, x))
return new_list
You can also use collections.Iterable to find iterable elements after you have checked for a list, if you wanted to only call map on strings you would use isinstance(x, basestring) what you check for all depends on what you are trying to achieve:
from collections import Iterable
def process_list(_list):
new_list = []
for x in _list:
if isinstance(x, list):
new_list.append(process_list(x))
elif isinstance(x, Iterable):
new_list.append(map(lambda y: y + 1, x))
else:
new_list.append(x+1)
return new_list
Than I want to write a function that achieves this purpose, if given:
t = [1,2,[2,2],[3,3]]
I want a function that makes t be
[1,2,4,6]. Here, is my code in Python:
t=[1,2,[2,2],[3,3]]
def nested_sum(t):
for x in t:
if type(t[x])=='int':
t[x]=t[x]
else:
t[x]=['sum(t[x])']
return t
nested_sum(t)
I got the error message that
Traceback (most recent call last):
File "nested_sum.py", line 11, in <module>
nested_sum(t)
File "nested_sum.py", line 5, in nested_sum
if type(t[x])=='int':
TypeError: list indices must be integers, not list
I am not quite sure about the mistake(s) I made. Since my logic is that:
type(t[0])=1 which is of "Int" type and type(t[2])=[2,2] which is of "List" type and I think these fulfills the "if...else..." statement.
Any help would be appreciated for pointing my mistakes. Thank you so much in advance.
You can put it in a single list comprehension using isinstance:
[sum(x) if isinstance(x,list) else x for x in t]
[1, 2, 4, 6]
You could use collection.Iterable which will work on any iterable like tuples etc..
t = [1,2,[2,2],[3,3],(4,5)]
from collections import Iterable
print [sum(x) if isinstance(x, Iterable) else x for x in t]
[1, 2, 4, 6, 9]
In the list comprehension, if x is an iterable/list we add the sum of the subelements or else we just take the element x
Using your own code, you would use enumerate to access the list elements using their index:
def nested_sum(t):
for ind, x in enumerate(t):
if type(t[ind])== int: # int not "int"
t[ind] = x
else:
t[ind] = sum(t[ind])
return t
In the code ind is the index of each subelement and x is the actual subelement
The problem with your code is that you are looping through the items of your list and not through the indexes. For making it work you should change your code like this:
def nested_sum(t):
for x in range(len(t)):
if type(t[x]) == int:
t[x] = t[x]
else:
t[x] = sum(t[x])
return t
Note also that int in type(t[x]) == int and sum(t[x]) in your else clause should not be strings.
Use sum() when x is a list, append it to res otherwise
t=[1,2,[2,2],[3,3]]
def nested_sum(l):
res = []
for x in l:
if type(x) == type([]):
res.append(sum(x))
elif type(x) == type(1):
res.append(x)
return res
nested_sum(t)
def nested_sum(t):
for index, item in enumerate(t):
if type(item) == list:
t[index] = sum(item)
return t
Explanation:
enumerate(t) returns (0, t[0]), (1, t[1]) etc.
Then, for each item in t, check if item is a list. If it is, replace it with the sum of all elements in that list. This is done in t[index] = sum(item).
An answer would be:
def nested_sum(mixed_list):
res = list()
for element in mixed_list:
try:
a = sum(element)
except TypeError:
a = element
res.append(a)
return res
Which works fine if the list contains numbers and lists of numbers.
Basically, I'm trying to flatten a list in my function but ignore that (you can also ignore the print functions I put in).
take x = [[1,2,3],4,5] to be my variable.
I call prob7(x) but the issue is that when type([1,2,3]) gets checked == list, it returns false. Why is that? I explicitly check this on the interpreter command line and it returns true. But inside the function, I get a false.
Just a bug that I missed because I'm sleepy or am I misunderstanding some part of the Python language? I run version 2.6 if it matters.
def prob7(list): # flatten a list
tempList = []
if list: # meaning if there are elements in the list and it is not empty
for i in list:
if type(i) != list:
print tempList,'if',i,type(i)==list
tempList.append(i)
else:
print tempList,'else',i
tempList.extend(prob7(i))
return tempList
Just not use 'list' as a variable name and use isinstance(var, list) instead of type(var) == list.
Please find corrected sample below.
def prob7(mylist): # flatten a list
tempList = []
if mylist: # meaning if there are elements in the list and it is not empty
for i in mylist:
if not isinstance(i, list):
print tempList, 'if', i, isinstance(i, list)
tempList.append(i)
else:
print tempList, 'else', i
tempList.extend(prob7(i))
return tempList
Or if you don't really required to use recursion and you don't care about values order then you can use something like this:
lVals = [[1,2,3],4,5, [1,[4,7]]]
def make_flat(mylist): # flatten a list
while any(isinstance(x, list) for x in mylist):
for i, val in enumerate(mylist):
if isinstance(val, list):
mylist.extend(mylist.pop(i))
break
return mylist
make_flat(lVals)
>>> [4, 5, 1, 2, 3, 1, 4, 7]
Artisom has your answer. In addtion, type checks are not very Pythonic. Duck typing often is the way to go. In case your elements are numbers only, the following does the job too, without explicit type checks but behavior checks:
def prob7(inlist): # flatten a list
outlist = []
for x in inlist:
try:
outlist += x
except TypeError:
outlist.append(x)
return outlist
Note that string elements in this implementation would behave like nested lists. Anyway, just wanted to illustrate what it means to expect behavior, not types.
Some alternate approaches:
# Iterative, but more functional-style
def flatten(a_list):
while any(isinstance(x, list) for x in a_list):
a_list = sum((x if isinstance(x, list) else [x] for x in a_list), [])
return a_list
# Using a generator recursively,
# then evaluating the generator to produce the list
# instead of explicitly appending each element.
def flatten_gen(a_list):
for x in a_list:
if isinstance(x, list):
for y in flatten_gen(x): yield y
else: yield x
def flatten(a_list): return list(flatten_gen(a_list))
The problem here is you are using a local variable name (list) that is the same as the global list type. You should change your variable name. Also, when checking types like that you can use the is operator.
type(l) is list
But here's my version of flatten.
def flatten(alist):
rv = []
for val in alist:
if isinstance(val, list):
rv.extend(flatten(val))
else:
rv.append(val)
return rv
This does not alter the original list, but returns a new list. This is consistent with most other patterns.