Best Pythonic way to conditionally append lists - python

I'm sure this question has come up before, but I couldn't find an exact example.
I have 2 lists and want to append the second to the first, only of the values are not already there.
So far I have working code, but was wondering if there were a better, more "Pythonic" was of doing this:
>>> list1
[1, 2, 3]
>>> list2
[2, 4]
>>> list1.extend([x for x in list2 if x not in list1])
>>> list1
[1, 2, 3, 4]
EDIT
Based on comments made, this code does not satisfy adding only once, ie:
>>> list1 = [1,2,3]
>>> list2 = [2,4,4,4]
>>> list1.extend([x for x in list2 if x not in list1])
>>> list1
[1, 2, 3, 4, 4, 4]
How would I end up with only:
[1, 2, 3, 4]

If you want to maintain the order, you can use collections.OrderedDict like this
from collections import OrderedDict
from itertools import chain
list1, list2 = [1, 2, 3], [2, 4]
print list(OrderedDict.fromkeys(chain(list1, list2)))
# [1, 2, 3, 4]
If the order of the elements is not important, you can use the set like this
from itertools import chain
list1, list2 = [1, 2, 3], [2, 4]
print list(set(chain(list1, list2)))
# [1, 2, 3, 4]

A way could be using built in type set:
list(set(list1).union(list2))
You would need to store the result of the operation, if you wanted to extend list1 then you can assign it to list1:
list1=list(set(list1).union(list2))
Note: Keep in mind that this approach may not keep the order of the elements in the list.
Hope this helps!

Related

Adjustable lambda to different lists

I am sorting different lists. For example:
One list has 3 integers: [[3, 4, 2], [3, 5, 3]]
The other has 4: [[3, 4, 2, 4], [3, 5,3 ,2]]
Like this there are more, ranging from two to five integers.
I could do this manually for every list, but since I'm still learning I could ask how this is done automatically!
So I have this code for rearranging the list in descending order
l.sort(key=lambda x: [x[0], x[1], x[2], x[3]], reverse = True)
This would work for the second example, but for the first example it would give:
IndexError: list index out of range
So my thought was something like this:
key = lambda x: [x[i] for i in range(len(l[0]))]
but it didn't work..
Does anybody knows if in this situation you can have an adjustable lambda?
I'm not quite sure if I get your question correctly, as you talk about one list in each case but then again show lists looking like lists of lists.
So for the simple case of a normal list, python provides sorting already, you don't need lambda:
>>> a = [3,2,7,1,11]
>>> b = sorted(a)
>>> b
[1, 2, 3, 7, 11]
>>> b = sorted(a, reverse=True)
>>> b
[11, 7, 3, 2, 1]
>>>
Check here for more options on sorting: https://docs.python.org/3/howto/sorting.html
Now if you have a list of lists, you could go for something like this:
>>> a = [[1,5,2],[3,4,1]]
>>> a
[[1, 5, 2], [3, 4, 1]]
>>> for idx,ls in enumerate(a):
a[idx] = sorted(ls, reverse=True)
>>> a
[[5, 2, 1], [4, 3, 1]]
which you could then use in a map function if you wanted to use a lambda:
>>> a = [[1,5,2],[3,4,1]]
>>> b = map(lambda x: sorted(x, reverse=True),a)
>>> b
<map object at 0x03C0F220>
>>> list(b)
[[5, 2, 1], [4, 3, 1]]
>>>
Note to put the list keyword infront of the map object to make it a list.

How to check what is the most common value in a specific index in list of lists

I have a list of list of integers:
ls = [[1,2,3],[2,2,3],[1,1,3]]
and I want to end up with a list that has len(ls[0]) elements, and each element is the element that appears most in that index. In this example the result would be [1 (appears in first&last), 2 (appears in first&second), 3 (appears in all)]
I tried using loops and was hoping for a built-in like all (most?)
ls = [[1,2,3],[2,2,3],[1,1,3]]
ls2=[]
ls2 =[item for sub in ls for item in sub]
ls2
Out[40]: [1, 2, 3, 2, 2, 3, 1, 1, 3]
ls3=[]
for item in len(ls):...
What would be the way to do so?
Transpose your list, apply Counters.
>>> from collections import Counter
>>> ls = [[1,2,3],[2,2,3],[1,1,3]]
>>> counts = [Counter(x) for x in zip(*ls)]
>>> [c.most_common(1)[0][0] for c in counts]
[1, 2, 3]
This will pick an arbitrary number if two numbers share the highest occurence for the same index.
my (alternative solution) 2 cents:
>>> ls = [[1, 2, 3], [2, 2, 3], [1, 1, 3]]
>>> l_mc = [max(x, key=x.count) for x in zip(*ls)]
>>> l_mc
[1, 2, 3]
Here is another way:
from collections import Counter
from operator import itemgetter
ls = [[1, 2, 3], [2, 2, 3], [1, 1, 3]]
print([max(x.items(), key = itemgetter(1))[0] for x in map(Counter, zip(*ls))])
# [1, 2, 3]

Why does assignment to python type 'list' work? [duplicate]

Newbie with a question, so please be gentle:
list = [1, 2, 3, 4, 5]
list2 = list
def fxn(list,list2):
for number in list:
print(number)
print(list)
list2.remove(number)
print("after remove list is ", list, " and list 2 is ", list2)
return list, list2
list, list2 = fxn(list, list2)
print("after fxn list is ", list)
print("after fxn list2 is ", list2)
This results in:
1
[1, 2, 3, 4, 5]
after remove list is [2, 3, 4, 5] and list 2 is [2, 3, 4, 5]
3
[2, 3, 4, 5]
after remove list is [2, 4, 5] and list 2 is [2, 4, 5]
5
[2, 4, 5]
after remove list is [2, 4] and list 2 is [2, 4]
after fxn list is [2, 4]
after fxn list2 is [2, 4]
I don't understand why list is changing when I am only doing list2.remove(), not list.remove(). I'm not even sure what search terms to use to figure it out.
The reason this is happening can be found here:
mlist = [1,2,3,4,5]
mlist2 = mlist
the second statement "points" mlist2 to mlist (i.e., they both refer to the same list object) and any changes you make to one is reflected in the other.
To make a copy instead try this (using a slice operation):
mlist = [1,2,3,4,5]
mlist2 = mlist[:]
In case you are curious about slice notation, this SO question Python Lists(Slice method) will give you more background.
Finally, it is not a good idea to use list as an identifier as Python already uses this identifier for its own data structure (which is the reason I added the "m" in front of the variable names)
That's because both list and list2 are referring to the same list after you did the assignment list2=list.
Try this to see if they are referring to the same objects or different:
id(list)
id(list2)
An example:
>>> list = [1, 2, 3, 4, 5]
>>> list2 = list
>>> id(list)
140496700844944
>>> id(list2)
140496700844944
>>> list.remove(3)
>>> list
[1, 2, 4, 5]
>>> list2
[1, 2, 4, 5]
If you really want to create a duplicate copy of list such that list2 doesn't refer to the original list but a copy of the list, use the slice operator:
list2 = list[:]
An example:
>>> list
[1, 2, 4, 5]
>>> list2
[1, 2, 4, 5]
>>> list = [1, 2, 3, 4, 5]
>>> list2 = list[:]
>>> id(list)
140496701034792
>>> id(list2)
140496701034864
>>> list.remove(3)
>>> list
[1, 2, 4, 5]
>>> list2
[1, 2, 3, 4, 5]
Also, don't use list as a variable name, because originally, list refers to the type list, but by defining your own list variable, you are hiding the original list that refers to the type list. Example:
>>> list
<type 'list'>
>>> type(list)
<type 'type'>
>>> list = [1, 2, 3, 4, 5]
>>> list
[1, 2, 3, 4, 5]
>>> type(list)
<type 'list'>

Retrieve only non-duplicate elements from a list

What is the best option to retrieve only non-duplicate elements from a Python list? Say I have the following list:
lst = [1, 2, 3, 2, 3, 4]
I would like to retrieve the following:
lst = [1, 4]
(2 and 3 are not unique in that list, so they don't get retrieved)
Use collections.Counter to get counts of items. Combine with a list comprehension to keep only those that have a count of one.
>>> from collections import Counter
>>> lst = [1, 2, 3, 2, 3, 4]
>>> [item for item, count in Counter(lst).items() if count == 1]
[1, 4]
This is a breeze with a list comprehension:
>>> lst = [1, 2, 3, 2, 3, 4]
>>> [x for x in lst if lst.count(x) == 1]
[1, 4]
>>>
Also, I recommend that you do not name a variable list--it overshadows the built-in.

python `in` keyword as a function used in a filter

is it possible to use the python keyword in in a filter? I know that binary, unary, assignment operations are equivalent to a function call.
such as
''!=3
is the same as
''.__ne__(3)
is there an analogous thing for the in function?
I want to do something like this. ..
filter( list1.__in__, list2 )
I guess this can be accomplished with writing the in function... but i just want to know if it is already built in or not.
filter( list1.__contains__, list2 )
is more cleanly written as:
[ v for v in list2 if v in list1 ]
and to show equivalence:
>>> list1 = [2, 4, 6, 8, 10]
>>> list2 = [1, 2, 3, 4, 5]
>>> [ v for v in list2 if v in list1 ]
[2, 4]
You are looking for __contains__.
>>> [1, 2, 3].__contains__(2)
True
>>> [1, 2, 3].__contains__(4)
False
And for what you want to do:
>>> list1 = [2, 4, 6, 8, 10]
>>> filter(list1.__contains__, [1, 2, 3, 4, 5])
[2, 4]
In general you should use the functions from the operator module, in this case it would be operator.contains.
But there is much more efficient way to do this by using sets:
In [1]: list1 = [2, 4, 6, 8, 10]
In [2]: list2 = [1, 2, 3, 4, 5]
In [3]: list(set(list1) & set(list2))
Out[3]: [2, 4]
Note: The & operator is the intersection.
A list comprehension, as in Dan D.'s answer, is definitely the best way to do this. In the more general case, though, where you want to use something like in or not in a function which takes another function as an argument, you can use a lambda function:
>>> list1 = [2, 4, 6, 8, 10]
>>> list2 = [1, 2, 3, 4, 5]
>>> in_check = lambda item: item in list1
>>> filter(in_check, list2)
[2, 4]
Again, I present this just for your general knowledge; the best way to handle this specific case is definitely with a list comprehension, as in Dan D.'s answer. More information on lambdas in Python is available here.

Categories