Merge pairs on common integer with restrictions - python

I need an efficient way to merge a list of nodes (pairs of integers).
The merge should happen only if there is one common number in the pair and the common number is on the first or the last position (otherwise its allready connected).
For example:
mylist = [[4, 3], [6, 3]]
merge_links(mylist) # should output [4, 3, 6]
another example:
mylist = [[4, 3], [6, 3], [6, 4]]
merge_links(mylist)
# should output again [4, 3, 6] because both 6 and 4 allready exist in array.
and yet another example:
mylist = [[4, 3], [6, 3], [6, 4], [6, 2], [7, 4], [4, 9]]
merge_links(mylist)
# should output [7, 4, 3, 6, 2]
# [4, 3] ✔
# [4, 3] + [6, 3] ✔ -> [4, 3, 6]
# [4, 3, 6] + [6, 3] ✘ both 6 and 3 exist in [4, 3, 6]
# [4, 3, 6] + [6, 4] ✘ both 6 and 4 exist in [4, 3, 6]
# [4, 3, 6] + [6, 2] ✔ -> [4, 3, 6, 2]
# [4, 3, 6, 2] + [7, 4] ✔ -> [7, 4, 3, 6, 2]
# [7, 4, 3, 6, 2] + [4, 9] ✘ 4 is allready connected "7-4-3"!
Currently I'm using:
def merge_links(a, b):
inter = np.intersect1d(a, b, True)
a = set(a) - set(inter)
b = set(b) - set(inter)
n = np.unique(list(a) + list(inter) + list(b))
return n
But it doesnt work well for the above restrictions

The code below works as follows:
Create a new list with size of old list + 1 (The maximum size the
output can reach).
Start with the first pair in your list of pairs with its first value
at the end of your new list, and its second value at the start of
your new list. And mark them as visited using a python dictionary
for example.
Maintain two indexes of your first and last positions pointing to
the end and start of your new list respectively initially.
Iterate on the rest of pairs, if for pair i its both values exist or
don't exist in the dictionary, skip it. Else, merge the not visited
value to element at the first index or the last index (depending on
the position of the visited value), and update your index. Note that
no merging will happen if the visited value is not at the first or
the last index (if it is somewhere in the middle).
Return a concatenation of new list[first index to end] with new
list[start to the last index].
Note: every merging operation takes O(1). Also you can use an array of booleans instead of dictionary if you know the range of pairs values.
#The function takes a list of pairs as an argument
def merge_lst(lst):
#Dictionary to check if value is already in array
visited = dict()
#Length of old list
lst_len = len(lst)
#New list will have at most lst_len+1 elements
new_lst = [0]*(lst_len+1)
#Put the first pair values in last and first elements of the new list repectively and mark them as visited
new_lst[lst_len], new_lst[0] = lst[0][0], lst[0][1]
visited[lst[0][0]], visited[lst[0][1]] = True, True
#Maintain the positions of your first and last elements, which are the the last index and 0 respectively now
first_index, last_index = lst_len, 0
#Iterate on the rest of pairs
for i in range(1, lst_len):
#Check if pair[a, b] are already visited
a_exists, b_exists = lst[i][0] in visited, lst[i][1] in visited
#Skip if they both exist or don't exist
if(a_exists == b_exists):
continue
#Assume a was the common one
common, to_merge = lst[i][0], lst[i][1]
#If b exists (b is the common not a), then just swap
if(b_exists):
common, to_merge = lst[i][1], lst[i][0]
#If the common element is at the first index, the first element and index are updated
if(new_lst[first_index] == common):
first_index-=1
new_lst[first_index] = to_merge
visited[to_merge] = True
#If the common element is at the last index, the last element and index are updated
elif(new_lst[last_index] == common):
last_index+=1
new_lst[last_index] = to_merge
visited[to_merge] = True
#Else, the common element is somewhre in the middle (already connected)
#Return concatenation of new_lst[first_index to the end] with new_lst[0 to the last_index]
return new_lst[first_index:lst_len+1]+new_lst[0:last_index+1]
This code gives correct output for all of your mentioned test cases.

Related

python list of lists common elements iteration

list= [[5, 4, 6], [6, 4, 5], [7, 1, 2]]
I have the above list, I want to compare every list index like [5,4,6] with [6,4,5] and [7,1,2] i.e. with every other list index
and for output: if there exist any common elements between 2 indexes in comparision then I want to output in a format
"the first element of each index" along with any common elements in the index.
answer for this iteration would be [5,4,6] as 5 is the first element of the index in comparison, 6 is the first element of the index in comparison, 4 is the common element.
next compare [6, 4, 5] with [5,4,6] and [7,1,2] and the answer would be [6,5,4]
next compare [7,1,2] with [5,4,6] and [6, 4, 5] and the answer would be [7]
Please help, I have been trying for really long.
basically I want every list index to check for common elements with every other list index and if 2 list indexes have anything in common I want to get a new output list with the first element of both list indexes and common elements
final output= [[5,6,4],[6,5,4],[7]]
myL = [[5, 4, 6], [6, 4, 5], [7, 1, 2]]
newLi = []
for i in range(len(myL)):
tmpLi = []
firstList = myL[i]
for a in range(len(myL)):
if a != i:
secondList = myL[a]
inCommon = set(firstList).intersection(secondList)
if len(inCommon) != 0:
tmpLi.append(firstList[0])
tmpLi.append(secondList[0])
for b in inCommon:
if b not in tmpLi:
tmpLi.append(b)
if len(tmpLi) == 0:
tmpLi.append(firstList[0])
newLi.append(tmpLi)
print(newLi)

How to create a custom List from existing List, based on condition in python?

I have a list
L= [1,3,3,4,4,5,5,7,8,9,9,9].
the rule for creating custom list is: current element in the main list (L) should be in less-than next number in main list(L) .
for example :
In list(L) starts from first element
1) 1 is less than next number 3 in the List (L) then add 1 to new list(new_list) then remove from main list(L)
2) next 3 is not less-than 3 we should skip now (next iteration we have to use this 3)
3) in current iteration 3 is compared to 4 i.e. 3 less than 4 then add 3 into (new_list) and remove from main list(L).
4) next compare 4 with next number 4 will not less than then skip
Like this I want to create number a custom list which contain list of possible custom lists.
my expected output after first iteration in new_list [[1,3,4,5,7,8,9]] and L= [3,4,5,9,9]
my final expected output will be like :[[1,3,4,5,7,8,9],[3,4,5,9],[9]]
Here is a way to do it :
result = []
while len(L)>0:
indexes_to_remove = []
result_temp = []
L = L_temp
for i in range(len(L)-1):
if L[i]<L[i+1]:
result_temp.append(L[i])
indexes_to_remove.append(i)
# Since there is a 9 at the end of each of your list I assume that
# you add the last value of your list at each iteration
if len(L)>0:
result_temp.append(L[len(L)-1])
indexes_to_remove.append(len(L)-1)
L_temp = [i for j, i in enumerate(L) if j not in indexes_to_remove]
result.append(result_temp)
print("L = {}; result = {}".format(L, result))
Output :
L = [1, 3, 3, 4, 4, 5, 5, 7, 8, 9, 9, 9]; result = [[1, 3, 4, 5, 7, 8, 9]]
L = [3, 4, 5, 9, 9]; result = [[1, 3, 4, 5, 7, 8, 9], [3, 4, 5, 9]]
L = [9]; result = [[1, 3, 4, 5, 7, 8, 9], [3, 4, 5, 9], [9]]
L = []; result = [[1, 3, 4, 5, 7, 8, 9], [3, 4, 5, 9], [9], []]
I want to identify number of custom list I can create from existing list .
Since you only want to identify the number of such lists you obtain at the end, and not the actual list, you can easily compute it as follows. Traverse the list and check for condition less than or equal to and increment the counter, until satisfied, if not satisfies, then reinitialise the counter to zero. And keep traversing. At the end print the maximum value of counter you obtained in any step, which will be the answer.

Slicing element from sublist - Python

I want to return the number 5 from this:
list_1 = [[1, 2, 3], [4, 5, 6]]
I thought this would work but it is not:
print(list_1[1:1])
It returns an empty list. It is Index 1 (second list) and position 1 (second number in the list).
Shouldn't that work?
You need two separate operations:
sub_list = list_1[1]
item = sub_list[1]
# or shortly list_1[1][1]
What you did was calling the slice interface which has an interface of [from:to:step]. So it meant: "give me all the items from index 1 to index 1" (read about slicing for more information).
list_1 = [[1, 2, 3], [4, 5, 6]]
then
list_1[0] == [1,2,3]
list_1[1] == [4,5,6]
then
list_1[1][1:1] == [] #slice starting from position '1', and around to the position before '1', effectively returning an empty list
list_1[1][1] == 5
edit corrections as from comments
list_1[1][1]
The first [1] gives you [4, 5, 6]. The next [1] gives you 5
Let's say we have a list:
list_1 = [[1, 2, 3], [4, 5, 6]]
You're question asks, why does
list_1[1:1]
Return []?
Because you're asking for the objects from index 1 to index 1, which is nothing.
Take a simple list:
>>> x = [1, 2, 3]
>>> x[1:1]
[]
This is also empty because you're asking for all objects from index 1 to index 1.
All the 2nd number does is say the maximum reach non-inclusive. So...
>>> x = [1, 2, 3]
>>> x[1:10000]
[2, 3]
>>> x[1:-1023]
[]
>>> x[1:2]
[2]
If the 1st number is equal to or greater than the 2nd number, you'll always end up with an empty list unless you change the step of the slice (if it's equal, it'll always be empty)
Of a 2-dimensional array, if you wanted the 2nd object of the 2nd list in the list:
>>> list_1[1]
[4, 5, 6]
>>> list_1[1][1]
5

Remove a column from a nested list in Python

I need help figuring how to work around removing a 'column' from a nested list to modify it.
Say I have
L = [[1,2,3,4],
[5,6,7,8],
[9,1,2,3]]
and I want to remove the second column (so values 2,6,1) to get:
L = [[1,3,4],
[5,7,8],
[9,2,3]]
I'm stuck with how to modify the list with just taking out a column. I've done something sort of like this before? Except we were printing it instead, and of course it wouldn't work in this case because I believe the break conflicts with the rest of the values I want in the list.
def L_break(L):
i = 0
while i < len(L):
k = 0
while k < len(L[i]):
print( L[i][k] , end = " ")
if k == 1:
break
k = k + 1
print()
i = i + 1
So, how would you go about modifying this nested list?
Is my mind in the right place comparing it to the code I have posted or does this require something different?
You can simply delete the appropriate element from each row using del:
L = [[1,2,3,4],
[5,6,7,8],
[9,1,2,3]]
for row in L:
del row[1] # 0 for column 1, 1 for column 2, etc.
print L
# outputs [[1, 3, 4], [5, 7, 8], [9, 2, 3]]
If you want to extract that column for later use, while removing it from the original list, use a list comprehension with pop:
>>> L = [[1,2,3,4],
... [5,6,7,8],
... [9,1,2,3]]
>>>
>>> [r.pop(1) for r in L]
[2, 6, 1]
>>> L
[[1, 3, 4], [5, 7, 8], [9, 2, 3]]
Otherwise, just loop over the list and delete the fields you no longer want, as in arshajii's answer
You can use operator.itemgetter, which is created for this very purpose.
from operator import itemgetter
getter = itemgetter(0, 2, 3) # Only indexes which are needed
print(list(map(list, map(getter, L))))
# [[1, 3, 4], [5, 7, 8], [9, 2, 3]]
You can use it in List comprehension like this
print([list(getter(item)) for item in L])
# [[1, 3, 4], [5, 7, 8], [9, 2, 3]]
You can also use nested List Comprehension, in which we skip the elements if the index is 1, like this
print([[item for index, item in enumerate(items) if index != 1] for items in L])
# [[1, 3, 4], [5, 7, 8], [9, 2, 3]]
Note: All these suggested in this answer will not affect the original list. They will generate new lists without the unwanted elements.
Use map-lambda:
print map(lambda x: x[:1]+x[2:], L)
Here is one way, updated to take in kojiro's advice.
>>> L[:] = [i[:1]+i[2:] for i in L]
>>> L
[[1, 3, 4], [5, 7, 8], [9, 2, 3]]
You can generalize this to remove any column:
def remove_column(matrix, column):
return [row[:column] + row[column+1:] for row in matrix]
# Remove 2nd column
copyofL = remove_column(L, 1) # Column is zero-base, so, 1=second column
when you do the del it will delete that index and reset the index, so you have to reduce that index. Here I use the count to reduce and reset the same from the index list we have. Hope this helps. Thanks
nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
remove_cols_index = [1,2]
count = 0
for i in remove_cols_index:
i = i-count
count = count+1
del nested_list[i]
print (nested_list)
[j.pop(1) for j in nested_list]
from https://www.geeksforgeeks.org/python-column-deletion-from-list-of-lists/

Merge two lists based on condition

I am trying to merge two lists based on position of index, so sort of a proximity intersection.
A set doesn't work in this case. What i am trying to do is match index in each list then if the element is one less than that of the element in other list, only then i collect it.
An example will explain my scenario better.
Sample Input:
print merge_list([[0, 1, 3], [1, 2], [4, 1, 3, 5]],
[[0, 2, 6], [1, 4], [2, 2], [4, 1, 6]])
Sample Output:
[[0,2],[4,6]]
so on position 0 in list1 we have 1, 3 and in list2 we have 2, 6. Since 1 is one less than 2, so we collect that and move on, now 3 is less than 6 but it's not one less than i.e. not 5 so we ignore that. Next we have [1, 2][1, 4], so both index/position 1, but 2 is not one less than 4 so we ignore that. Next we have [2, 2] in list2 both index 2 doesn't match any index in first list so no comparison. Finally we have [4, 1, 3, 5] [4, 1, 6] comparison. Both index match and only 5 in list one is one less than list two so we collect six hence we collect [4,6] meaning index 4 and match etc.
I have tried to make it work, but i don't seem to make it work.
This is my code so far.
def merge_list(my_list1, my_list2):
merged_list = []
bigger_list = []
smaller_list = []
temp_outer_index = 0
temp_inner_index = 0
if(len(my_list1) > len(my_list2)):
bigger_list = my_list1
smaller_list = my_list2
elif(len(my_list2) > len(my_list1)):
bigger_list = my_list2
smaller_list = my_list1
else:
bigger_list = my_list1
smaller_list = my_list2
for i, sublist in enumerate(bigger_list):
for index1 , val in enumerate(sublist):
for k, sublist2 in enumerate(smaller_list):
for index2, val2 in enumerate(sublist2):
temp_outer_index = index1 + 1
temp_inner_index = index2 + 1
if(temp_inner_index < len(sublist2) and temp_outer_index < len(sublist)):
# print "temp_outer:%s , temp_inner:%s, sublist[temp_outer]:%s, sublist2[temp_inner_index]:%s" % (temp_outer_index, temp_inner_index, sublist[temp_outer_index], sublist2[temp_inner_index])
if(sublist2[temp_inner_index] < sublist[temp_outer_index]):
merged_list.append(sublist[temp_outer_index])
break
return merged_list
No clue what you are doing, but this should work.
First, convert the list of lists to a mapping of indices to set of digits contained in that list:
def convert_list(l):
return dict((sublist[0], set(sublist[1:])) for sublist in l)
This will make the lists a lot easier to work with:
>>> convert_list([[0, 1, 3], [1, 2], [4, 1, 3, 5]])
{0: set([1, 3]), 1: set([2]), 4: set([1, 3, 5])}
>>> convert_list([[0, 2, 6], [1, 4], [2, 2], [4, 1, 6]])
{0: set([2, 6]), 1: set([4]), 2: set([2]), 4: set([1, 6])}
Now the merge_lists function can be written as such:
def merge_lists(l1, l2):
result = []
d1 = convert_list(l1)
d2 = convert_list(l2)
for index, l2_nums in d2.items():
if index not in d1:
#no matching index
continue
l1_nums = d1[index]
sub_nums = [l2_num for l2_num in l2_nums if l2_num - 1 in l1_nums]
if sub_nums:
result.append([index] + sorted(list(sub_nums)))
return result
Works for your test case:
>>> print merge_lists([[0, 1, 3], [1, 2], [4, 1, 3, 5]],
[[0, 2, 6], [1, 4], [2, 2], [4, 1, 6]])
[[0, 2], [4, 6]]
I believe this does what you want it to do:
import itertools
def to_dict(lst):
dct = {sub[0]: sub[1:] for sub in lst}
return dct
def merge_dicts(a, b):
result = []
overlapping_keys = set.intersection(set(a.keys()), set(b.keys()))
for key in overlapping_keys:
temp = [key] # initialize sublist with index
for i, j in itertools.product(a[key], b[key]):
if i == j - 1:
temp.append(j)
if len(temp) > 1: # if the sublist has anything besides the index
result.append(temp)
return result
dict1 = to_dict([[0, 1, 3], [1, 2], [4, 1, 3, 5]])
dict2 = to_dict([[0, 2, 6], [1, 4], [2, 2], [4, 1, 6]])
result = merge_dicts(dict1, dict2)
print(result)
Result:
[[0, 2], [4, 6]]
First, we convert your lists to dicts because they're easier to work with (this separates the key out from the other values). Then, we look for the keys that exist in both dicts (in the example, this is 0, 1, 4) and look at all pairs of values between the two dicts for each key (in the example, 1,2; 1,6; 3,2; 3,6; 2,4; 1,1; 1,6; 3,1; 3,6; 5,1; 5,6). Whenever the first element of a pair is one less than the second element, we add the second element to our temp list. If the temp list ends up containing anything besides the key (i.e. is longer than 1), we add it to the result list, which we eventually return.
(It just occurred to me that this has pretty bad performance characteristics - quadratic in the length of the sublists - so you might want to use Claudiu's answer instead if your sublists are going to be long. If they're going to be short, though, I think the cost of initializing a set is large enough that my solution might be faster.)
def merge_list(a, b):
d = dict((val[0], set(val[1:])) for val in a)
result = []
for val in b:
k = val[0]
if k in d:
match = [x for x in val[1:] if x - 1 in d[k]]
if match:
result.append([k] + match)
return result
Similar to the other answers, this will first convert one of the lists to a dictionary with the first element of each inner list as the key and the remainder of the list as the value. Then we walk through the other list and if the first element exists as a key in the dictionary, we find all values that meet your criteria using the list comprehension and if there were any, add an entry to the result list which is returned at the end.

Categories