python 2-D list how to make a set [duplicate] - python

This question already has answers here:
How to remove duplicate lists in a list of list? [duplicate]
(2 answers)
Closed 6 years ago.
as a python list follow
list1 = [[1,2],[3,4],[1,2]]
I want make a set so I can the unique list items like
list2 = [[1,2],[3,4]].
Is there some function in python I can use. Thanks

That will do:
>>> list1 = [[1,2],[3,4],[1,2]]
>>> list2 = list(map(list, set(map(tuple,list1))))
>>> list2
[[1, 2], [3, 4]]

Unfortunately, there is not a single built-in function that can handle this. Lists are "unhashable" (see this SO post). So you cannot have a set of list in Python.
But tuples are hashable:
l = [[1, 2], [3, 4], [1, 2]]
s = {tuple(x) for x in l}
print(s)
# out: {(1, 2), (3, 4)}
Of course, this won't help you if you want to later, say, append to these lists inside your main data structure, as they are now all tuples. If you absolutely must have the original list functionality, you can check out this code recipe for uniquification by Tim Peters.

Note that this only removes duplicate sublists, it does not take into account the sublist's individual elements. Ex: [[1,2,3], [1,2], [1]] -> [[1,2,3], [1,2], [1]]
>>> print map(list, {tuple(sublist) for sublist in list1})
[[1, 2], [3, 4]]

You can try this:
list1 = [[1,2],[3,4],[1,2]]
list2 = []
for i in list1:
if i not in list2:
list2.append(i)
print(list2)
[[1, 2], [3, 4]]

The most typical solutions have already been posted, so let's give a new one:
Python 2.x
list1 = [[1, 2], [3, 4], [1, 2]]
list2 = {str(v): v for v in list1}.values()
Python 3.x
list1 = [[1, 2], [3, 4], [1, 2]]
list2 = list({str(v): v for v in list1}.values())

There is no inbuilt single function to achieve this. You have received many answers. In addition to those, you may also use a lambda function to achieve this:
list(map(list, set(map(lambda i: tuple(i), list1))))

Related

Find duplicates within and outside nested list in python

I have a list s:
s=[[1,2,1],[2,2,1],[1,2,1]]
Case 1: Remove the duplicate groups in the list
Case 2: Remove the duplicate values within the groups
Desired Result:
Case 1 : [[1,2,1],[2,2,1]]
Case 2 : [[1,2],[2,1],[1,2]]
I tried using the list(set(s)) but it throws up an error:
unhashable type: 'list'
IIUC,
Case 1:
Convert the lists to tuple for hashing, then apply a set on the list of tuples to remove the duplicates. Finally, convert back to lists.
out1 = list(map(list, set(map(tuple, s))))
# [[1, 2, 1], [2, 2, 1]]
Case 2:
For each sublist, remove the duplicates while keeping order with conversion to dictionary keys (that are unique), then back to list:
out2 = [list(dict.fromkeys(l)) for l in s]
# [[1, 2], [2, 1], [1, 2]]
You need to know that it's not possible in Python to have a set of lists. The reason is that lists are not hashable. The simplest way to do your task in the first case is to use a new list of lists without duplicates such as below:
temp_list = []
for each_element in [[1,2,1],[2,2,1],[1,2,1]]:
if each_element not in temp_list:
temp_set.append(each_element)
print(temp_list)
The output:
[[1, 2, 1], [2, 2, 1]]
The case2 is more simple:
temp_list = []
for each_element in [[1,2,1],[2,2,1],[1,2,1]]:
temp_list.append(list(set(each_element)))
print(temp_list)
And this is the output:
[[1, 2], [1, 2], [1, 2]]
However these codes are not the pythonic way of doing things, they are very simple to be understood by beginners.

Appending variable that is modified in a for loop doesn't work as expected [duplicate]

This question already has answers here:
List on python appending always the same value [duplicate]
(5 answers)
Closed 4 years ago.
I have this code:
lst = []
given = [1, 2, 3, 4, 5]
result = []
for item in given:
lst.append(item)
print(lst)
result.append(lst)
print(result)
My expected result is [[1], [1, 2], [1, 2, 3], ...], but displayed result is [[1, 2, 3, 4, 5], ...] with 12345 repeated 5 times. What is wrong?
lst printed is as expected, which is [1] for the first loop, [1, 2] for the second loop, and so on.
Python doesn't create copy of lst every time when you append it to result, it just inserts reference. As a result you get list with N references to same list.
To create a copy of lst you can use lst.copy(). Also list slice operator works same lst[:].
Shortened version of your code:
given = [1, 2, 3, 4, 5]
result = [given[0 : i + 1] for i in range(len(given))]
print(result)
Result:
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
The problem is that you are appending the list as such which is equivalent to appending the reference object to the original list. Therefore, whenever the original list is modified, the changes are reflected in the places where the reference is created, in this case in result. As you keep iterating via the for loop, all your references appended in result keep getting updated with the latest value of lst. The final result is that at the end of the for loop, you have appended 5 references to the original list lst and all of them store the latest value of lst being [1,2,3,4,5].
There are several ways to avoid this. What you need is to copy only the values. One of them is to use lst[:]. other way is to use lst.copy()
for item in given:
lst.append(item)
print(lst)
result.append(lst[:])
print (result)
# [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
List is a mutable data type, there is only one copy in memory for a list unless you explicitly copy it to another variable. So
result.append(lst)
just appends a reference of the real copy and all the refercences point to the same copy.
In conclusion, you should learn about mutable/immutable data types and reference count in python.
Append lst.copy() gives the right output.
lst = []
given = [1,2,3,4,5]
result = []
for item in given:
lst.append(item)
print(lst)
result.append(lst.copy())
print(result)

List Multiplication: repeat paired elements and de-reference

I've found numerous solutions on how to repeat a List of Lists while flattening the repeated elements, like so:
[[1,2] for x in range(3)]
: [[1, 2], [1, 2], [1, 2]]
However, I actually have a list of objects that need to be repeated in-line in the List, like so:
mylist = [var1, var2, var1, var2, va1, var2]
without the extra inner list, and importantly, var1 should be copied, not referenced, such that changes to mylist[0] will not also change mylist[2] & mylist[4] etc.
Other solutions regarding lists only still leave the list elements as “pointers”, meaning editing one element actually alters all the repeats of that element in the list.
Is there a readable one-liner for using multiplication/comprehension to do this while removing the “pointers” in the list?
This is an operation users may have to do often with my class and a for() loop will make construction of their list much less readable. I'm already sad that we can't just use [var1, var2] * 30 without overloading __mult__, which I'm trying to avoid. That would have made their code so readable.
For example, the following use of deepcopy() is not good enough, as the objects are referenced and thus unexpectedly altered
>>> obj1=[1]; obj2=[200]
>>> a=deepcopy( 3*[obj1, obj2] )
>>> a
[[1], [200], [1], [200], [1], [200]]
>>> a[0][0]=50
>>> a
[[50], [200], [50], [200], [50], [200]]
the 50 propagated throughout the list, rather than changing only the first element.
You need
import copy
[copy.copy(y) for x in range(3) for y in [var1, var2]]
Or shallow copy is not enough
[copy.deepcopy(y) for x in range(3) for y in [var1, var2]]
I'm not quite sure what behavior you're looking for. Depending on what you want, one of these two options might be right:
In [0]: from itertools import chain
In [1]: from copy import deepcopy
In [2]: [deepcopy(x) for x in 3*[var1, var2]]
Out[2]: [[1, 2], [3, 4], [1, 2], [3, 4], [1, 2], [3, 4]]
In [3]: list( chain( *(3*[var1, var2]) ) )
Out[3]: [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]

mapping the append function to a list

I want to do the following elegantly. I have a list:
list1 = [[1,2],[3,1,4,7],[5],[7,8]]
I'd like to append the number 1 to each element of the list, so that I have
list1 = [[1,2,1],[3,1,4,7,1],[5,1],[7,8,1]]
I'm trying to map this via
map(list.append([1]), vectors)
but this returns the error append() takes exactly one argument (0 given) and if I just try append([1]) (without list.), I get NameError: global name 'append' is not defined. I guess I could do it with a loop, but this seems more elegant, is there a way to map this correctly?
Here is a several ways to implement what you want:
More readable and classic way
for el in list1:
el.append(1)
List comprehension
list1 = [el + [1] for el in list1]
Generators:
list1 = (el + [1] for el in list1)
Map
list1 = map(lambda el: el + [1], list1)
What to use?
It depends on you own situation and may depends on execution speed optimizations, code readability, place of usage.
Map is a worst choice in case of readability and execution speed
For is a fastest and more plain way to do this
Generators allows you to generate new list only when you really need this
List comprehension - one liner for classic for and it takes advantage when you need quickly filter the new list using if
i.e. if you need only add element to each item - for loop is a best choice to do this, but if you need add item only if item > 40, then you may consider to use List comprehension.
For example:
Classic For
x = 41
for el in list1:
if x > 40:
el.append(x)
List comprehension
x = 1
list1 = [el + [x] for el in list1 if x > 40]
as #jmd_dk mentioned, in this sample is one fundamental difference: with simple for you can just append to an existing object of the list which makes much less impact to execution time and memory usage. When you use List comprehension, you will get new list object and in this case new list object for each item.
Try a list comprehension, taking advantage of the fact the adding lists concats them together.
new_list = [l + [1] for l in list1]
You can simply do
list1 = [[1,2],[3,1,4,7],[5],[7,8]]
for el in list1:
el.append(1)
map(lambda x: x + [1], list1)
you mean this?
list.append() have NO return (mean always return None)
With list comprehension and append, you can do:
list1 = [[1, 2], [3, 1, 4, 7], [5], [7, 8]]
[item.append(1) for item in list1]
print(list1) # Output: [[1, 2, 1], [3, 1, 4, 7, 1], [5, 1], [7, 8, 1]]
Output:
>>> list1 = [[1, 2], [3, 1, 4, 7], [5], [7, 8]]
>>> [item.append(1) for item in list1]
[None, None, None, None]
>>> list1
[[1, 2, 1], [3, 1, 4, 7, 1], [5, 1], [7, 8, 1]]
You may also use extend like this:
[item.extend([1]) for item in list1]
print(list1) # Output: [[1, 2, 1], [3, 1, 4, 7, 1], [5, 1], [7, 8, 1]]

A pythonic way to remove all instances of an element without generating a new list

There are many questions similar to this (here is one) but the solutions I've seen use list comprehension or filter and those ways generate a new list (or a new iterator in the case of filter in Python 3.x). There are solutions that remove instances by modifying the list itself (like this, and this), and those would be my last resort. However, I am asking to see if there a more elegant ("Pythonic" as another question calls it) way of doing it.
Why I am disregarding solutions that generate a new list: I am iterating over a collection of lists, and Python allows me to modify the "current" list I am iterating over, but not to replace it entirely:
>>> ll= [[1,2,3], [2,3,4], [4,5,6]]
>>> for l in ll:
if l[0] == 2:
l = [10]
>>> ll
[[1, 2, 3], [2, 3, 4], [4, 5, 6]] #replacement didn't happen
>>> for l in ll:
if l[0] == 2:
l.remove(2)
>>> ll
[[1, 2, 3], [3, 4], [4, 5, 6]] #modification succeeded
You need to use slice assignment to replace all list elements instead of the list itself:
for l in ll:
if l[0] == 2:
l[:] = [10]
The [:] part turns this into slice assignment; instead of replacing the reference l points to, you replace all elements contained in l with the new list.
Demo:
>>> ll= [[1,2,3], [2,3,4], [4,5,6]]
>>> for l in ll:
... if l[0] == 2:
... l[:] = [10]
...
>>> ll
[[1, 2, 3], [10], [4, 5, 6]]

Categories