I am trying to print a unique set of arrays from an array while keeping the array in order.
Example array:
[[1,2,3], [1,3,2], [1,2,3], [3,1,2]]
The output that I am looking for is:
[[1,2,3], [1,3,2], [3,1,2]]
I've looked at converting the array into a set but it appears that it has issues with a 2D array (I believe it compares each element individually which if it could run, it would result in simply [1,2,3]) also, I am reading that it does not keep the order of the values.
You need simple check if an element in the rest of your array and get rid of it. To preserve order, loop should go backwards:
array = [[1,2,3], [1,3,2], [1,2,3], [3,1,2]]
for a_ind in range((len(array)-1), 0, -1):
if (array[a_ind] in array[:a_ind]):
array.pop(a_ind)
print (array)
# [[1, 2, 3], [1, 3, 2], [3, 1, 2]]
Use a nested for-loop to see if an element has been seen already:
data = [[1,2,3], [1,3,2], [1,2,3], [3,1,2]]
processed = []
for i in range(len(data)):
for j in range(i):
if data[i] == data[j]:
break
else:
processed.append(data[i])
print(processed) # Should be [[1,2,3], [1,3,2], [3,1,2]]
Try something like this:
>>> u = [[1,2,3], [1,3,2], [1,2,3], [3,1,2]]
>>> unique = []
>>> (unique.append(x) for x in u if x not in unique)
<generator object <genexpr> at 0x103df4620>
>>> print(unique)
[[1, 2, 3], [1, 3, 2], [3, 1, 2]]
Solution 1: using dictionary to get rid of the lists that got the same elements
a = [[1, 2, 3], [1, 3, 2], [1, 2, 3], [3, 1, 2]]
d = {}
for k in a:
d[str(k)] = k
a = []
for v in d.values():
a += [v]
print(a)
output*
[[1, 3, 2], [1, 2, 3], [3, 1, 2]]
using a dictionary, this one can have the lists in the list in a different order each time you execute the script.
Solution 2: looking into the list to pop the one that got the same order of elements
b = [[1, 2, 3], [1, 3, 2], [1, 2, 3], [3, 1, 2]]
for l in b:
if l in b[b.index(l) + 1:]:
b.pop(b.index(l))
print(b)
output
[[1, 3, 2], [1, 2, 3], [3, 1, 2]]
Instead of doing for loops, you can:
class Foo(object):
def __init__(self, a_list):
self._list = list(a_list)
def __str__(self):
return str(self._list)
def __repr__(self):
return self.__str__()
def __eq__(self, other):
return all([i1 == i2 for i1, i2 in zip(self, other)])
def __ne__(self, other):
return any([i1 != i2 for i1, i2 in zip(self, other)])
def __hash__(self):
return 0
def __iter__(self):
return iter(self._list)
a1 = Foo([1, 2, 3])
a2 = Foo([1, 3, 2])
a3 = Foo([1, 2, 3])
a4 = Foo([3, 2, 1])
c = [a1, a2, a3, a4]
print(list(set(c)))
# Output
[[1, 2, 3], [1, 3, 2], [3, 2, 1]]
__str__ and __repr__ methods are for the output and __iter__ for the list comprehension.
Online: https://repl.it/BqAS/0
Related
Let's use a simple example: say I have a list of lists
ll = [[1, 2], [1, 3], [2, 3], [1, 2, 3], [2, 3, 4]]
and I want to find all longest lists, which means all lists that maximize the len function. Of course we can do
def func(x):
return len(x)
maxlen = func(max(ll, key=lambda x: func(x)))
res = [l for l in ll if func(l) == maxlen]
print(res)
Output
[[1, 2, 3], [2, 3, 4]]
But I wonder if there are more efficient way to do this, especially when the function is very expensive or the list is very long. Any suggestions?
From a computer science/algorithms perspective, this is a very classical "reduce" problem.
so, pseudocode. It's honestly very straightforward.
metric():= a mapping from elements to non-negative numbers
winner = []
maxmetric = 0
for element in ll:
if metric(element) larger than maxmetric:
winner = [ element ]
maxmetric = metric(element)
else if metric(element) equal to maxmetric:
append element to winner
when the function is very expensive
Note that you do compute func(x) for each element twice, first there
maxlen = func(max(ll, key=lambda x: func(x)))
then there
res = [l for l in ll if func(l) == maxlen]
so it would be beneficial to store what was already computed. functools.lru_cache allow that easily just replace
def func(x):
return len(x)
using
import functools
#functools.lru_cache(maxsize=None)
def func(x):
return len(x)
However, beware as due to way how data are stored argument(s) must be hashable, so in your example you would first need convert list e.g. to tuples i.e.
ll = [(1, 2), (1, 3), (2, 3), (1, 2, 3), (2, 3, 4)]
See descripiton in docs for further discussion
Is not OK use dictionary like below, (this is O(n))
ll = [[1, 2], [1, 3], [2, 3], [1, 2, 3], [2, 3, 4]]
from collections import defaultdict
dct = defaultdict(list)
for l in ll:
dct[len(l)].append(l)
dct[max(dct)]
Output:
[[1, 2, 3], [2, 3, 4]]
>>> dct
defaultdict(list, {2: [[1, 2], [1, 3], [2, 3]], 3: [[1, 2, 3], [2, 3, 4]]})
OR use setdefault and without defaultdict like below:
ll = [[1, 2], [1, 3], [2, 3], [1, 2, 3], [2, 3, 4]]
dct = {}
for l in ll:
dct.setdefault(len(l), []).append(l)
Output:
>>> dct
{2: [[1, 2], [1, 3], [2, 3]], 3: [[1, 2, 3], [2, 3, 4]]}
I'd like to remove same lists in a list having len(a) = 5 and a = [[1,2,3],[2,3,4], [0,1,2],[2,4,6],[3,6,9]] as results.
How can I get that?
a1 = [[1,2,3],[2,3,4]]
a2 = [[0,1,2],[2,4,6]]
a3=[[1,2,3],[0,1,2],[3,6,9]]
a = a1+a2+a3
a = [tuple(l) for l in a]
print(set(a))
print(len(a))
a=[list(ele) for ele in a]
print(a)
print(len(a))
You cannot make a set of lists of lists because they are not hashable. You could first convert them to tuples and then create a set:
a1 = [[1,2,3],[2,3,4]]
a2 = [[0,1,2],[2,4,6]]
a3=[[1,2,3],[0,1,2],[3,6,9]]
a = a1+a2+a3
a = [list(x) for x in set([tuple(L) for L in a])]
output:
[[0, 1, 2], [2, 4, 6], [1, 2, 3], [2, 3, 4], [3, 6, 9]]
I have a list of lists, but some lists are "sublists" of other lists. What I want to do is remove the sublists from the larger list so that we only have the largest unique sublists.
For example:
>>> some_list = [[1], [1, 2], [1, 2, 3], [1, 4]]
>>> ideal_list = [[1, 2, 3], [1, 4]]
The code that I've written right now is:
new_list = []
for i in range(some_list)):
for j in range(i + 1, len(some_list)):
count = 0
for k in some_list[i]:
if k in some_list[j]:
count += 1
if count == len(some_list[i]):
new_list.append(some_list[j])
The basic algorithm that I had in mind is that we'd check if a list's elements were in the following sublists, and if so then we use the other larger sublist. It doesn't give the desired output (it actually gives [[1, 2], [1, 2, 3], [1, 4], [1, 2, 3]]) and I'm wondering what I could do to achieve what I want.
I don't want to use sets because duplicate elements matter.
Same idea as set, but using Counter instead. It should be a lot more efficient in sublist check part than brute force
from collections import Counter
new_list = []
counters = []
for arr in sorted(some_list, key=len, reverse=True):
arr_counter = Counter(arr)
if any((c & arr_counter) == arr_counter for c in counters):
continue # it is a sublist of something else
new_list.append(arr)
counters.append(arr_counter)
With some inspiration from #mkrieger1's comment, one possible solution would be:
def merge_sublists(some_list):
new_list = []
for i in range(len(some_list)):
true_or_false = []
for j in range(len(some_list)):
if some_list[j] == some_list[i]:
continue
true_or_false.append(all([x in some_list[j] for x in some_list[i]]))
if not any(true_or_false):
new_list.append(some_list[i])
return new_list
As is stated in the comment, a brute-force solution would be to loop through each element and check if it's a sublist of any other sublist. If it's not, then append it to the new list.
Test cases:
>>> merge_sublists([[1], [1, 2], [1, 2, 3], [1, 4]])
[[1, 2, 3], [1, 4]]
>>> merge_sublists([[1, 2, 3], [4, 5], [3, 4]])
[[1, 2, 3], [4, 5], [3, 4]]
Input:
l = [[1], [1, 2], [1, 2, 3], [1, 4]]
One way here:
l1 = l.copy()
for i in l:
for j in l:
if set(i).issubset(set(j)) and i!=j:
l1.remove(i)
break
This prints:
print(l1)
[[1, 2, 3], [1, 4]]
EDIT: (Taking care of duplicates as well)
l1 = [list(tupl) for tupl in {tuple(item) for item in l }]
l2 = l1.copy()
for i in l1:
for j in l1:
if set(i).issubset(set(j)) and i!=j:
l2.remove(i)
break
I didn't manage to correct a code I thought it would work for sure. Any advice to make the code functional is accepted.
Expected outputs of the following code is a list containing a cyclic permuation of the list
l = [1,2,3,4] (i.e : [[4, 1, 2, 3],[3, 4, 1, 2],[2, 3, 4, 1],[1, 2, 3, 4]])
Although what I get is : [[2, 3, 4, 1]]
The code :
def cycGen(l):
L=[]
while not(l in L) :
L.append(l)
for i in range(len(l)):
if l[i] == len(l) :
l[i]=1
else :
l[i] = 1 + l[i]
return L
print(cycGen([1,2,3,4]))
Another variation of the solution is to consider the following code wich seems unfortunatly not working either :
def cycGen(l):
L=[]
for k in range(len(l)):
L.append(l)
for i in range(len(l)):
if l[i] == len(l) :
l[i]=1
else :
l[i] = 1 + l[i]
return L
Help me with your generous knowlege sharing please.
You can use collections.deque:
from collections import deque
a = [1, 2, 3, 4]
d = deque(a)
for _ in range(len(a)):
d.rotate()
print(list(d))
Which gives you the output:
[4, 1, 2, 3]
[3, 4, 1, 2]
[2, 3, 4, 1]
[1, 2, 3, 4]
As mentioned in Efficient way to shift a list in python
An easy way is just:
In [12]: x = [1,2,3,4]
In [13]: [x[i:]+x[:i] for i in range(len(x))]
Out[13]: [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
In your first code sample, the line L.append(l) appends a "reference" (loosely speaking) to the list l to the end of L, rather than appending a copy as you seem to be expecting. Thus, when l is later modified, the reference to it contained in L is modified as well, and so when l in L is tested, l will equal the reference to itself in L, and so the loop will end. The same basic problem causes your second code sample to return multiples of the same list rather than several different lists.
To store a copy of l at the current point in time in L instead, use L.append(l[:]).
Here is an easy way:
>>> def cycGen(l):
size = len(l)
return [[l[(i+j)%size] for i in range(size)] for j in range(size)]
>>> l = [1,2,3,4]
>>> print cycGen(l)
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
You could do this using a generator too:
a = [1, 2, 3, 4]
def next_pos(max):
i = 0
while True:
for n in xrange(max):
yield n + i
i += 1
pos = next_pos(len(a))
b = []
for i in xrange(len(a)):
n = []
for j in xrange(len(a)):
m = pos.next()
if m >= len(a):
m -= len(a)
n.append(a[m])
b.append(n)
print b
output:
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
Let's say I have a list:
l = [0,1,2,3,4]
And I want to obtain a sequence of lists in this logic:
[[1,2,3,4],[0,1,2,3],[2,3,4],[1,2,3],[0,1,2],[3,4],[2,3],[1,2],[0,1],[0],[1],[2],[3],[4]]
That's it, sublists made of l[1:] and l[:-1]
I started by this recursive function:
l = [0,1,2,3,4]
def sublist(l):
if len(l) == 1:
return l
else:
return [sublist(l[1:]),sublist(l[:-1])]
a = [sublist(l)]
print a
But it's not really what I what as it outputs:
[[[[[[4], [3]], [[3], [2]]], [[[3], [2]], [[2], [1]]]], [[[[3], [2]], [[2], [1]]], [[[2], [1]], [[1], [0]]]]]]
import itertools
[list(itertools.combinations(l, x)) for x in range(1, len(l))]
Here's a very straightforward implementation:
def sublists_n(l, n):
subs = []
for i in range(len(l)-n+1):
subs.extend([l[i:i+n]])
return subs
def sublists(l):
subs = []
for i in range(len(l)-1,0,-1):
subs.extend(sublists_n(l,i))
return subs
>>> l = [0,1,2,3,4]
>>> sublists(l)
[[0, 1, 2, 3], [1, 2, 3, 4], [0, 1, 2], [1, 2, 3], [2, 3, 4], [0, 1], [1, 2], [2, 3], [3, 4], [0], [1], [2], [3], [4]]
[l[x:] for x in range(len(l))] + [l[:x+1] for x in range(len(l))]
Loops through l twice, but you sort of have to no matter what I think (could use zip but same thing).
A simple recursion, doesn't quite order things correctly but its simple.
def sublists(l):
right = l[1:]
left = l[:-1]
result = [right, left]
if len(l) > 2:
result.extend(sublists(right))
result.extend(sublists(left))
return result
print sublists([0,1,2,3,4])