Related
I am trying to remove multiple occurrences of a value from a list.The output does not remove any of the desired items.
def rem(a,li):
try:
while a in li == True:
li.remove(a)
print('Updated list: ',li)
except ValueError:
print(a,' is not located in the list ',li)
Example to trying out function:
L = [1,2,3,45,3,2,3,3,4,5]
rem(2,L)
Output: Updated list: [1, 2, 3, 45, 3, 2, 3, 3, 4, 5]
There are 2 mistakes in your code. The first one is
while a in li == True: In fact, this check always returns False since li == True is False.
It should actually be while (a in li) == True:, or while a in li:
Also, if you are trying to delete only repeated occurrences of a (i.e. leave the first occurrence of a in) then list comprehension won't suit your needs. You will have to add an additional check inside your rem() function that catches the first occurrence of a and then executes your loop:
def rem(a, li):
list_length = len(li)
i = 0
while (li[i] != a) and (i < list_length):
i += 1 # skip to the first occurrence of a in li
i += 1 # increment i
while i < list_length:
if li[i] == a:
del li[i]
print('Updated list: ', li)
list_length -= 1 # decrement list length after removing occurrence of a
else:
i += 1
The code snippet above does not cover the edge cases where the list is empty, or the case a is not in the list. I'll leave those exercises to you.
Try changing the while condition to while a in li
def rem(a,li):
try:
while a in li:
li.remove(a)
print('Updated list: ',li)
except ValueError:
print(a,' is not located in the list ',li)
L = [1,2,3,45,3,2,3,3,4,5]
rem(2,L)
In general, if you want to remove duplicates from a list then you can use the built-in set.
Assuming you want to remove all instances of a from L, you could also just use a simple list comprehension:
def rem(a,li):
return [x for x in li if x != a]
L = [1,2,3,45,3,2,3,3,4,5]
print(rem(2,L))
Which Outputs:
[1, 3, 45, 3, 3, 3, 4, 5]
That would be a better job for a list comprehension.
L[:] = [a for a in L if a not in (2,)]
Assigning to a slice will mutate the list in place.
I'm updating my answer to account for the various interpretations your question allows
and also to make it more general by accepting also strings and multiple values to be removed at once.
def removed(items, original_list, only_duplicates=False, inplace=False):
"""By default removes given items from original_list and returns
a new list. Optionally only removes duplicates of `items` or modifies
given list in place.
"""
if not hasattr(items, '__iter__') or isinstance(items, str):
items = [items]
if only_duplicates:
result = []
for item in original_list:
if item not in items or item not in result:
result.append(item)
else:
result = [item for item in original_list if item not in items]
if inplace:
original_list[:] = result
else:
return result
Docstring extension:
"""
Examples:
---------
>>>li1 = [1, 2, 3, 4, 4, 5, 5]
>>>removed(4, li1)
[1, 2, 3, 5, 5]
>>>removed((4,5), li1)
[1, 2, 3]
>>>removed((4,5), li1, only_duplicates=True)
[1, 2, 3, 4, 5]
# remove all duplicates by passing original_list also to `items`.:
>>>removed(li1, li1, only_duplicates=True)
[1, 2, 3, 4, 5]
# inplace:
>>>removed((4,5), li1, only_duplicates=True, inplace=True)
>>>li1
[1, 2, 3, 4, 5]
>>>li2 =['abc', 'def', 'def', 'ghi', 'ghi']
>>>removed(('def', 'ghi'), li2, only_duplicates=True, inplace=True)
>>>li2
['abc', 'def', 'ghi']
"""
You should be clear about what you really want to do, modify an existing list, or make a new list with
the specific items missing. It's important to make that distinction in case you have a second reference pointing
to the existing list. If you have, for example...
li1 = [1, 2, 3, 4, 4, 5, 5]
li2 = li1
# then rebind li1 to the new list without the value 4
li1 = removed(4, li1)
# you end up with two separate lists where li2 is still pointing to the
# original
li2
# [1, 2, 3, 4, 4, 5, 5]
li1
# [1, 2, 3, 5, 5]
This may or may not be the behaviour you want.
Just keep track of index no and use del
Simple approach:
L = [1,2,3,45,3,2,3,3,4,5]
def rem(a,li):
for j,i in enumerate(li):
if a==i:
del li[j]
return li
print(rem(2,L))
output:
[1, 3, 45, 3, 3, 3, 4, 5]
I'm trying to write a function that would combine two lists while removing duplicate items, but in a pure functional way.
For example:
a = [1,2,2]
b = [1,3,3,4,5,0]
union(a,b) --> [1,2,3,4,5,0]
The imperative form of the code would be:
def union(a,b):
c = []
for i in a + b:
if i not in c:
c.append(i)
return c
I've tried several approaches, but couldn't find a way to do that without using a loop to go over the items - what am I missing?
list(set(a + b))
This combines two lists a and b and using set takes only unique vales and then we can make it back to list.
If you want to keep the order you can use collections.OrderedDict, otherwise just use set. These data structures use hash values of their items for preserving them, thus they don't keep the duplicates.
In [11]: from collections import OrderedDict
In [12]: list(OrderedDict.fromkeys(a+b))
Out[12]: [1, 2, 3, 4, 5, 0]
Have you tried using sets?
>>> a = [1,2,2]
>>> b = [1,3,3,4,5,0]
>>> list(set(a).union(set(b)))
[0, 1, 2, 3, 4, 5]
To combine the two lists:
a = [1,2,2]
b = [1,3,3,4,5,0]
Using sets:
union = set(a) | set(b)
# -> set([0, 1, 2, 3, 4, 5])
Using comprehension list:
union = a + [x for x in b if x not in a]
# -> [1, 2, 2, 3, 3, 4, 5, 0]
Using loop, without duplicates, preserving order:
union = []
for x in a + b:
if x not in union:
union.append(x)
# -> [1, 2, 3, 4, 5, 0]
How about
>>> x = [1,2,3]
>>> y = [1,3,5,7,9]
>>> list(set(x).union(set(y)))
If you don't mind importing libraries use toolz for that (http://toolz.readthedocs.io/en/latest/index.html), which is generally a valuable and widely used helper for functional programming with Python. It has a function named unique (http://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.unique), that does exactly what you want.
from toolz.itertoolz import unique
a = [1,2,2,3,0]
b = [1,3,3,4,5,0]
c = a + b # could do that with itertools.chain() in a more functional way
return list(unique(c))
# -> [1, 2, 3, 0, 4, 5]
I want the output of returned values from a function to be extended to a set of lists.
def func():
return [-1,2],[4,1]
f=[1,2]
g=[3,4]
f,g=func() #How make it append
print f,g
#Expected Output:
[1,2,-1,2] [3,4,4,1]
For one list, I can do f.extend(func()) but what about multiple lists.
Creating temporary variables is not preferred.
Why not pass the function two parameters
def func(l1 ,l2):
return l1+[-1,2],l2+[4,1]
f = [1,2]
g = [3,4]
f,g = func(f,g)
print f,g
produces
[1, 2, -1, 2] [3, 4, 4, 1]
However, it may be easier to let the function extend the lists and avoid the return value altogether
def func(l1 ,l2):
l1.extend([-1,2])
l2.extend([4,1])
f = [1,2]
g = [3,4]
func(f,g)
print f,g
If this question is more general, and you cannot modify your function, use zip:
>>> f,g = (x+y for x,y in zip((f,g), func()))
>>> f
[1, 2, -1, 2]
>>> g
[3, 4, 4, 1]
I hope to write the join_lists function to take an arbitrary number of lists and concatenate them. For example, if the inputs are
m = [1, 2, 3]
n = [4, 5, 6]
o = [7, 8, 9]
then we I call print join_lists(m, n, o), it will return [1, 2, 3, 4, 5, 6, 7, 8, 9]. I realize I should use *args as the argument in join_lists, but not sure how to concatenate an arbitrary number of lists. Thanks.
Although you can use something which invokes __add__ sequentially, that is very much the wrong thing (for starters you end up creating as many new lists as there are lists in your input, which ends up having quadratic complexity).
The standard tool is itertools.chain:
def concatenate(*lists):
return itertools.chain(*lists)
or
def concatenate(*lists):
return itertools.chain.from_iterable(lists)
This will return a generator which yields each element of the lists in sequence. If you need it as a list, use list: list(itertools.chain.from_iterable(lists))
If you insist on doing this "by hand", then use extend:
def concatenate(*lists):
newlist = []
for l in lists: newlist.extend(l)
return newlist
Actually, don't use extend like that - it's still inefficient, because it has to keep extending the original list. The "right" way (it's still really the wrong way):
def concatenate(*lists):
lengths = map(len,lists)
newlen = sum(lengths)
newlist = [None]*newlen
start = 0
end = 0
for l,n in zip(lists,lengths):
end+=n
newlist[start:end] = list
start+=n
return newlist
http://ideone.com/Mi3UyL
You'll note that this still ends up doing as many copy operations as there are total slots in the lists. So, this isn't any better than using list(chain.from_iterable(lists)), and is probably worse, because list can make use of optimisations at the C level.
Finally, here's a version using extend (suboptimal) in one line, using reduce:
concatenate = lambda *lists: reduce((lambda a,b: a.extend(b) or a),lists,[])
One way would be this (using reduce) because I currently feel functional:
import operator
from functools import reduce
def concatenate(*lists):
return reduce(operator.add, lists)
However, a better functional method is given in Marcin's answer:
from itertools import chain
def concatenate(*lists):
return chain(*lists)
although you might as well use itertools.chain(*iterable_of_lists) directly.
A procedural way:
def concatenate(*lists):
new_list = []
for i in lists:
new_list.extend(i)
return new_list
A golfed version: j=lambda*x:sum(x,[]) (do not actually use this).
You can use sum() with an empty list as the start argument:
def join_lists(*lists):
return sum(lists, [])
For example:
>>> join_lists([1, 2, 3], [4, 5, 6])
[1, 2, 3, 4, 5, 6]
Another way:
>>> m = [1, 2, 3]
>>> n = [4, 5, 6]
>>> o = [7, 8, 9]
>>> p = []
>>> for (i, j, k) in (m, n, o):
... p.append(i)
... p.append(j)
... p.append(k)
...
>>> p
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
This seems to work just fine:
def join_lists(*args):
output = []
for lst in args:
output += lst
return output
It returns a new list with all the items of the previous lists. Is using + not appropriate for this kind of list processing?
Or you could be logical instead, making a variable (here 'z') equal to the first list passed to the 'join_lists' function
then assigning the items in the list (not the list itself) to a new list to which you'll then be able add the elements of the other lists:
m = [1, 2, 3]
n = [4, 5, 6]
o = [7, 8, 9]
def join_lists(*x):
z = [x[0]]
for i in range(len(z)):
new_list = z[i]
for item in x:
if item != z:
new_list += (item)
return new_list
then
print (join_lists(m, n ,o)
would output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Using Python
I want the following:
[1, 2, 2, 1, 2, 3, 2, 3]
To be transformed into:
[1, 2, 2, [1, 2, 3], 2, 3]
Rules: Go through each item in the list. If we hit a 2 followed by a 1 created a list and include that 1 in that list until we hit a 3, include that 3, then close the list and continue. It's like if 1 was 3 were parenthesis.
I'm not very good with recursive algorithms which I think might be needed in this case.
Thanks as always.
Still keeping in mind that #Walter is correct in his comments to your question, this is a silly implementation of what you asked for, inspired by the final bit of your question, in which you suggest that 1 and 3 could just be replaced with [1 and 3].
>>> import re
>>> s = repr([1, 2, 2, 1, 2, 3, 2, 3])
>>> s = re.sub('1', '[1', s)
>>> s = re.sub('3', '3]', s)
>>> l = eval(s)
>>> l
[[1, 2, 2, [1, 2, 3], 2, 3]]
What it does is working on the representation of the list (a string) and substituting the way you suggested using regular expressions. Finally, it evaluate the string (getting back a list).
I call this implementation "silly" because it does the trick, but it's ugly and truly unpythonic. That said, it does the trick, so if you are simply using it for a one-off conversion of some data you need to use...
HTH!
def whatever(a):
b = []
tmp = []
last = None
for elem in a:
if tmp:
tmp.append(elem)
if elem == 3:
b.append(tmp)
tmp = []
elif last == 2 and elem == 1:
tmp.append(1)
else:
b.append(elem)
last = elem
return b
print whatever([1, 2, 2, 1, 2, 3, 2, 3])
That is an entertaining problem! Here is my solution:
def treeize(treeizable, tree=None, stopper=object()):
if tree is None:
tree = []
if treeizable[:1] == [stopper]:
tree.append(treeizable.pop(0))
return tree
elif treeizable[0:2] == [2, 1]:
tree.append(treeizable.pop(0))
subtree = []
treeize(treeizable, subtree, stopper=3)
tree.append(subtree)
return treeize(treeizable, tree, stopper)
elif treeizable:
tree.append(treeizable.pop(0))
return treeize(treeizable, tree, stopper)
else:
return tree
This function receives a flat list treeizable that should be converted to a nested list tree. The stopper parameter marks when the current list is done - being this a nested or the toplevel list. (Since the default value of stopper is an instance of object, it is impossible that there will be a stopper on a list called with the default value, because instances of object are different between themselves).
def treeize(treeizable, tree=None, stopper=object()):
For easing our work, the default value of tree is None and, if it has the default value, then it is set to a list. It is made because it is problematic to have mutable values as default parameter objects. Also, it would be annoying to have to type the function with an empty list everytime.
if tree is None:
tree = []
If the first value of the flat list is the "stopper", then it is added to the tree and the tree is returned. Note that, by using treeizable.pop(0) I am actually removing the value from the flat list. Since the stopper is only set when defining a nested list so, when we found it, no more need to be done: the "subtree" (that is, the nested list) is complete. Also, note that I get the first element of the list with a slice of the list. I made it because it is boring to have to type if treeizable and treeizable[0] == stopper. Since slicing does not have problems with inexistent indexes, I got the slice and compared it to another list made in place with only the stopper:
if treeizable[:1] == [stopper]:
tree.append(treeizable.pop(0))
return tree
If the beginning of the list is the "beginner", then I pop the first element from the list, append it to the tree and create a new tree - that is, a empty list. Now I call treeize() with the remaining list and the empty subtree, also passing 3 as the stopper value. treeize() will recursively generate a new tree that I append to my initial tree. After that, just call treeize() with the remaining of the list (which does not contain the elements of the subtree anymore) and the original list. Note that the stopper should be the same stopper received by the original call.
elif treeizable[0:2] == [2, 1]:
tree.append(treeizable.pop(0))
subtree = []
treeize(treeizable, subtree, stopper=3)
tree.append(subtree)
return treeize(treeizable, tree, stopper)
If none of the previous conditions (the first element is a stopper, the beginning of the list is [2, 1]) is true, then I verify if there is something in the list. In this case, I pop the first element, add to the tree and call treeize() with the rest of the list.
elif treeizable:
tree.append(treeizable.pop(0))
return treeize(treeizable, tree, stopper)
In the case that not even the previous condition is true... then we have an empty list. This means that all elements were put in the tree. Just return the tree to the user:
else:
return tree
This seems to have worked:
>>> treeize.treeize([1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 3, 2, 4, 5, 3, 3, 2, 3, 4])
[1, 2, 2, [1, 2, 2, [1, 2, 2, [1, 2, 3], 2, 4, 5, 3], 3], 2, 3, 4]
Your question has a taste of homework. In principle, we should not answer it but it is so interesting that I couldn't help myself :) If it is a homework, however, do not try to use this solution as your because it would be wrong and your teacher would surely find it on Google :P
I like state machines:
from itertools import izip, tee
def pairwise(iterable):
a, b = tee(iterable)
next(b)
return izip(a, b)
class Flat(object):
def append_next(self, alist, e0, e1):
alist.append(e0)
if e0 == 2 and e1 == 1:
alist.append([])
self.__class__ = Nested
def append_last(self, alist, e):
alist.append(e)
class Nested(object):
def append_next(self, alist, e0, e1):
alist[-1].append(e0)
if e0 == 3:
self.__class__ = Flat
def append_last(self, alist, e):
alist[-1].append(e)
def nested(flat_list):
if len(flat_list) <= 1:
return list(flat_list)
state = Flat()
nested_list = []
for x, y in pairwise(flat_list):
state.append_next(nested_list, x, y)
state.append_last(nested_list, y)
return nested_list
s = [1, 2, 2, 1, 2, 3, 2, 3]
print nested(s)
gives:
[1, 2, 2, [1, 2, 3], 2, 3]
But this might be more pythonic:
def nested(flat_list):
if len(flat_list) <= 1:
return list(flat_list)
pairs = pairwise(flat_list)
nested_list = []
while True:
for x, y in pairs:
nested_list.append(x)
if x == 2 and y == 1:
nested_list.append([])
break
else:
nested_list.append(y)
break
for x, y in pairs:
nested_list[-1].append(x)
if x == 3:
break
else:
nested_list[-1].append(y)
break
return nested_list
Pease bear with me - it is 2:50(night) - here is my version - not very beatiful but it works pretty well for me:
def buildNewList(inputList):
last = 0
res = []
for i,c in enumerate(inputList):
if i == 0:
prev = c
if i < last:
continue
if c == 1 and prev == 2:
if 3 in inputList[i:]:
last = i + 1 + inputList[i:].index(3)
res.append(buildNewList(inputList[i: last]))
else:
last = len(inputList)
res.append(buildNewList(inputList[i:len(inputList)]))
else:
res.append(c)
prev = c
return res
l1 = buildNewList([1, 2, 2, 1, 2, 3, 2, 3])
>>> [1, 2, 2, [1, 2, 3], 2, 3]
l2 = buildNewList([1, 2, 2, 1, 2, 3, 2, 1, 2, 3])
>>> [1, 2, 2, [1, 2, 3], 2, [1, 2, 3]]
l3 = buildNewList([1,2,3,1,2,3])
>>> [1, 2, 3, 1, 2, 3]
l4 = buildNewList([1,2,1,1,2,1])
>>> [1, 2, [1, 1, 2, [1]]]