Consider this list of lists:
l = [ [1], [1], [1,2,3], [4,1], [5], [5], [6], [7,8,9], [7,6], [8,5] ]
I want to combine all the lists that have at least one number in common, that will be done iteratively until it finished and no dubletters will go with. The result will be:
combine(l) = [ [1,2,3,4], [5,6,7,8,9] ]
is there any neat way to do this, perhaps with itertools?
out = []
for l in lists:
for o in out:
if set(l).intersection(set(o)):
o[:] = list(set(l) + set(o)) # Mutate, don't reassign temp var
break
else:
out.append(l)
Not perfectly written, could be optimized and is not sorted, but should give you the idea of how to do it.
Maybe this?
l = [ [1], [1], [1,2,3], [4,1], [5], [5], [6], [7,8,9], [7,6], [8,5] ]
a, b = [], map(set, l)
while len(a) != len(b):
a, b = b, []
for x in a:
for i, p in enumerate(b):
if p & x:
b[i] = p | x
break
else:
b.append(x)
print a
# [set([1, 2, 3, 4]), set([5, 6, 7, 8, 9])]
My naive attempt:
Example:
def combine_helper(l):
l = map(set, l)
for i, x in enumerate(l, 1):
x = set(x)
for y in l[i:]:
if x & y:
x = x | y
yield tuple(sorted(x))
def combine(l):
last_l = []
new_l = l
while last_l != new_l:
last_l = new_l
new_l = list(set(combine_helper(last_l)))
return map(list, last_l)
l = [ [1], [1], [1,2,3], [4,1], [5], [5], [6], [7,8,9], [7,6], [8,5] ]
print combine(l)
Output:
$ python test.py
[[1, 2, 3, 4], [5, 6, 7, 8, 9]]
Recursion to the rescue! And don't forget reduce!
input_list = [ [1], [1], [1, 2, 3], [4, 1], [5], [5], [6], [7, 8, 9],
[7, 6], [8, 5] ]
def combine(input_list):
input_list = map(set, input_list) # working with sets has some advantages
reduced_list = reduce(combine_reduce, input_list, [])
if len(reduced_list) == len(input_list):
# return the whole thing in the original format (sorted lists)
return map(sorted, map(list, reduced_list))
else:
# recursion happens here
return combine(reduced_list)
def combine_reduce(reduced_list, numbers):
'''
find the set to add the numbers to or append as a new set.
'''
for sub_set in reduced_list:
if sub_set.intersection(numbers):
sub_set.update(numbers)
return reduced_list
reduced_list.append(numbers)
return reduced_list
print combine(input_list)
Prints out:
$ python combine.py
[[1, 2, 3, 4], [5, 6, 7, 8, 9]]
We have two things going on here. The first is reduce
: I'm using it to cook the list down, by fitting each element into the resulting list somewhere or appending it, if that didn't work. This does not do the whole job, though, so we repeat this process (recursion!) until reducing does not provide a shorter list.
Also, use of set
allows for the handy intersection
method. You will notice the line with map(set, input_list)
is redundant in recursion. Extracting a wrapper function combine
from the inner function combine_inner
and placing the formatting / unformatting (from list
to set
and back) in the outer function is left as an exercise.
Its possible to introduce the following attempt:
[{1}, {2}, {3}]
and the first element in the l list is [1,3]
, then all sets containing 1 and 3 in the output list should be joined: output = [{1,3}, {2}]
. Code:
l = [ [1], [1], [1,2,3], [4,1], [5], [5], [6], [7,8,9], [7,6], [8,5] ]
def compose(l):
# the following will convert l into a list of 1-element sets:
# e.g. [ {1}, {2}, ... ]
r = sum(l, [])
r = map(lambda x: set([x]), set(r))
# for every item in l
# find matching sets in r and join them together
for item in map(set, l):
outside = [x for x in r if not x & item] # elements untouched
inside = [x for x in r if x & item] # elements to join
inside = set([]).union(*inside) # compose sets
r = outside + [inside]
return r
Example:
>>> compose(l)
[set([1, 2, 3, 4]), set([8, 9, 5, 6, 7])]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.