I am looking for some help comparing the order of 2 Python lists, list1 and list2, to detect when list2 is out of order.
list1 is static and contains the strings a,b,c,d,e,f,g,h,i,j. This is the "correct" order.
list2 contains the same strings, but the order and the number of strings may change. (e.g. a,b,f,d,e,g,c,h,i,j or a,b,c,d,e)
I am looking for an efficient way to detect when list2 is our of order by comparing it against list1.
For example, if list2 is a,c,d,e,g,i should return true (as the strings are in order)
While, if list2 is a,d,b,c,e should return false (as string d appears out of order)
First, let's define list1:
>>> list1='a,b,c,d,e,f,g,h,i,j'.split(',')
>>> list1
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
While your list1 happens to be in alphabetical order, we will not assume that. This code works regardless.
Now, let's create a list2 that is out-of-order:
>>> list2 = 'a,b,f,d,e,g,c,h,i,j'.split(',')
>>> list2
['a', 'b', 'f', 'd', 'e', 'g', 'c', 'h', 'i', 'j']
Here is how to test whether list2 is out of order or not:
>>> list2 == sorted(list2, key=lambda c: list1.index(c))
False
False means out-of-order.
Here is an example that is in order:
>>> list2 = 'a,b,d,e'.split(',')
>>> list2 == sorted(list2, key=lambda c: list1.index(c))
True
True means in-order.
Ignoring elements of list1 not in list2
Let's consider a list2 that has an element not in list1:
>>> list2 = 'a,b,d,d,e,z'.split(',')
To ignore the unwanted element, let's create list2b:
>>> list2b = [c for c in list2 if c in list1]
We can then test as before:
>>> list2b == sorted(list2b, key=lambda c: list1.index(c))
True
Alternative not using sorted
>>> list2b = ['a', 'b', 'd', 'd', 'e']
>>> indices = [list1.index(c) for c in list2b]
>>> all(c <= indices[i+1] for i, c in enumerate(indices[:-1]))
True
Why do you need to compare it to list1 since it seems like list1 is in alphabetical order? Can't you do the following?
def is_sorted(alist):
return alist == sorted(alist)
print is_sorted(['a','c','d','e','g','i'])
# True
print is_sorted(['a','d','b','c','e'])
# False
Here's a solution that runs in expected linear time. That isn't too important if list1 is always 10 elements and list2 isn't any longer, but with longer lists, solutions based on index will experience extreme slowdowns.
First, we preprocess list1 so we can quickly find the index of any element. (If we have multiple list2s, we can do this once and then use the preprocessed output to quickly determine whether multiple list2s are sorted):
list1_indices = {item: i for i, item in enumerate(list1)}
Then, we check whether each element of list2 has a lower index in list1 than the next element of list2:
is_sorted = all(list1_indices[x] < list1_indices[y] for x, y in zip(list2, list2[1:]))
We can do better with itertools.izip and itertools.islice to avoid materializing the whole zip list, letting us save a substantial amount of work if we detect that list2 is out of order early in the list:
# On Python 3, we can just use zip. islice is still needed, though.
from itertools import izip, islice
is_sorted = all(list1_indices[x] < list1_indices[y]
for x, y in izip(list2, islice(list2, 1, None)))
is_sorted = not any(list1.index(list2[i]) > list1.index(list2[i+1]) for i in range(len(list2)-1))
The function any returns true if any of the items in an iterable are true. I combined this with a generator expression that loops through all the values of list2 and makes sure they're in order according to list1.
if list2 == sorted(list2,key=lambda element:list1.index(element)):
print('sorted')
Let's assume that when you are writing that list1 is strings a,b,c,d,e,f,g,h,i that this means that a could be 'zebra' and string b could actually be 'elephant' so the order may not be alphabetical. Also, this approach will return false if an item is in list2 but not in list1.
good_list2 = ['a','c','d','e','g','i']
bad_list2 = ['a','d','b','c','e']
def verify(somelist):
list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
while len(list1) > 0:
try:
list1 = list1[:list1.index(somelist.pop())]
except ValueError:
return False
return True
Related
I have 2 lists list1 and list2. How would I rearrange list1 (without rearranging list2) so that there are no matches on any positions eg:
UNDESIRED: list1 = [‘A’, ‘B’, ‘C’] list2 = [‘X’, ‘B’, ‘Z’] as you can see the B is on the same position, 1, in both lists…so I would then like to rearrange list1 to list1 = [‘B’, ‘A’, ‘C’] or any other order where there are no positional matches with list2 WITHOUT rearranging list2
You could do this with the help of the itertools module. There may be better / more efficient mechanisms. Note that the process() function will return None when there is no possible solution.
import itertools
def process(L1, L2):
for s in set(itertools.permutations(L1, len(L1))):
if all([a != b for a, b in zip(s, L2)]):
return s
return None
list1 = ['A', 'B', 'C']
list2 = ['X', 'B', 'Z']
print(process(list1, list2))
from itertools import permutations
def diff(list1, list2):
for item in permutations(list1, len(list1)):
if all(map(lambda a, b: a!=b, item, list2)):
return list(item)
return None
list1 = ['A', 'B', 'C']
list2 = ['X', 'B', 'Z']
result = diff(list1, list2)
if result:
print(result, 'vs', list2)
['A', 'C', 'B'] vs ['X', 'B', 'Z']
I solved it using this piece of code - however, as noted above, there are many possible cases where this very solution may not work. In this very case, you can try this:
import random
list1 = ['A', 'B', 'C']
list2 = ['X', 'B', 'Z']
for i in list1:
for j in list2:
if i == j in list2:
while list1.index(i) == list2.index(j):
list1.remove(i)
z = len(list1)
rand = random.choice(range(z))
list1.insert(rand, i)
print(list1, list2)
Also, note that you use quite weird apostrophes inside the list: ‘ instead of ' or ". AFAIK, Python won't be able to comprehend them correctly.
I'm hvaing a hard time with a problem which I thought was pretty simple.
I have two lists:
A small one, that looks like this:
list1 = ['A', 'B', 'C', 'D','E']
The second list is much bigger, going on for about 800 elements. It looks like this:
list2 = ['E', 'B', 'F', 'A', 'C', 'N'...]
I want to scan list2 and see if all of its elements are the same as the ones in list1. If they are different, I want to see which are the elements that differ and cancel them from list2. In this example, I want to print "F" and "N" from list2 and cancel them.
I tried:
found = False
lenght2 = len(list2)
i = 0
for j in list1:
for i in range(0, lenght2):
if i != j:
found = True
#I don't know how to cancel i
print(i)
i = i + 1
break
However, the whole thing does not work.
Is there anyone who could help me?
You could go through all of list2 then check if it is in list one, like this:
for i in range(len(list2)):
if list2[i] in list1:
pass
else:
#Cancel list2[i] ? Or whatever.
Keep it simple:
There's no need to use nested loops here -- you are removing elements from list2, based on information from list1, so loop through list2.
list1 = ['A', 'B', 'C', 'D', 'E']
list2 = ['E', 'B', 'F', 'A', 'C', 'N']
for e in list2[:]:
if e not in list1:
list2.remove(e)
print(list2)
This code loops through list2 and checks for each element (e) if it occurs in list1. If not, it removes it from list2.
Note: the use of list2[:] here rather than list2 is due to this.
Or use a list comprehension:
list1 = ['A', 'B', 'C', 'D', 'E']
list2 = ['E', 'B', 'F', 'A', 'C', 'N']
list2 = [e for e in list2 if e in list1]
print(list2)
See more about how list comprehensions work here.
In both cases, you should get:
['E', 'B', 'A', 'C']
I am creating a list of lists and want to prevent dupes. For example, I have:
mainlist = [[a,b],[c,d],[a,d]]
the next item (list) to be added is [b,a] which is considered a duplicate of [a,b].
UPDATE
mainlist = [[a,b],[c,d],[a,d]]
swap = [b,a]
for item in mainlist:
if set(item) & set(swap):
print "match was found", item
else:
mainlist.append(swap)
Any suggestions as to how I can test whether the next item to be added is already in the list?
Here's an approach using frozensets within a set to check for duplicates. It's a bit ugly since I'm invoking a function that works with global variables.
def add_to_mainlist(new_list):
if frozenset(new_list) not in dups:
mainlist.append(new_list)
mainlist = [['a', 'b'],['c', 'd'],['a', 'd']]
dups = set()
for l in mainlist:
dups.add(frozenset(l))
print("Before:", mainlist)
add_to_mainlist(['a', 'b'])
print("After:", mainlist)
This outputs:
Before: [['a', 'b'], ['c', 'd'], ['a', 'd']]
After: [['a', 'b'], ['c', 'd'], ['a', 'd']]
Showing that the new list was indeed not added to the original.
Here's a cleaner version that calculates the existing set on the fly inside a function that does everything locally:
def add_to_mainlist(mainlist, new_list):
dups = set()
for l in mainlist:
dups.add(frozenset(l))
if frozenset(new_list) not in dups:
mainlist.append(new_list)
return mainlist
mainlist = [['a', 'b'],['c', 'd'],['a', 'd']]
print("Before:", mainlist)
mainlist = add_to_mainlist(mainlist, ['a', 'b']) # the assignment isn't needed, but done anyway :-)
print("After:", mainlist)
Why doesn't your existing code work?
This is what you're doing:
...
for item in mainlist:
if set(item) & set(swap):
print "match was found", item
else:
mainlist.append(swap)
You're intersecting two sets and checking the truthiness of the result. While this might be okay for 0 intersections, in the event that even one of the elements are common (example, ['a', 'b'] and ['b', 'd']), you'd still declare a match which is false.
Ideally you'd want to check the length of the resultant set and make sure its length is equal to than 2:
dups = False
for item in mainlist:
if len(set(item) & set(swap)) == 2:
dups = True
break
if dups == False:
mainlist.append(swap)
You'd also ideally want a flag to ensure that you did not find duplicates. Your previous code would add without checking all items first.
If the order of your inner lists doesn't matter, then this can trivially be accomplished using frozenset()s:
>>> mainlist = [['a', 'b'],['c', 'd'],['a', 'd']]
>>> mainlist = [frozenset(sublist) for sublist in mainlist]
>>>
>>> def add_to_list(lst, sublist):
... if frozenset(sublist) not in lst:
... lst.append(frozenset(sublist))
...
>>> mainlist
[frozenset({'a', 'b'}), frozenset({'d', 'c'}), frozenset({'a', 'd'})]
>>> add_to_list(mainlist, ['b', 'a'])
>>> mainlist
[frozenset({'a', 'b'}), frozenset({'d', 'c'}), frozenset({'a', 'd'})]
>>>
If the order does matter you can either do what #Coldspeed suggested - Construct a set() from your list, construct a frozenset() from the list to be added, and test for membership - or you can use all() and sorted() to test if the list to be added is equivalent to any of the other lists:
>>> def add_to_list(lst, sublist):
... for l in lst:
... if all(a == b for a, b in zip(sorted(sublist), sorted(l))):
... return
... lst.append(sublist)
...
>>> mainlist
[['a', 'b'], ['c', 'd'], ['a', 'd']]
>>> add_to_list(mainlist, ['b', 'a'])
>>> mainlist
[['a', 'b'], ['c', 'd'], ['a', 'd']]
>>>
I'm having some problems with an exercise about strings in python.
I have 2 different lists:
list1= "ABCDEFABCDEF"
and
list2= "AZBYCXDWEVFABCDEF"
I need to compare those 2 lists according to their position so the 1 letter together, then the 2...using the min length (so here length of list1) and store the letters in a new variable according to if they are different or the same.
identicals=[]
different=[]
I tried to code something and it seems to find the same ones, but doesn't work on the different ones since it copies them multiple times.
for x in list1:
for y in list2:
if list1>list2:
if x==y:
identicals.append(x)
if x!=y :
different.append(x)
if list2>list1:
if y==x:
identicals.append(y)
if y!=x:
different.append(y)
EDIT: Output result should be something like this:
identicals=['A']
different=["Z","B","Y","C","X","D","W","E","V",F","A"]
The thing is that the letter A is only shown on identicals but not in different even if F!=A.
You are getting unwanted duplicates because you have a nested pair of for loops, so each item in list2 get tested for every item in list1.
The key idea is to iterate over the two strings in parallel. You can do that with the built-in zip function, which yields a tuple of the corresponding items from each iterable you feed it, stopping as soon as one of the iterables runs out of items.
From your example code, it looks like you want to take the items for the different list from the longer string. To do that efficiently, figure out which string is the longer before you start looping.
I've renamed your strings because it's confusing to give strings a name starting with "list".
s1 = "ABCDEFABCDEF"
s2 = "AZBYCXDWEVFABCDEF"
identicals = []
different = []
small, large = (s1, s2) if len(s1) <= len(s2) else (s2, s1)
for x, y in zip(small, large):
if x == y:
identicals.append(y)
else:
different.append(y)
print(identicals)
print(different)
output
['A']
['Z', 'B', 'Y', 'C', 'X', 'D', 'W', 'E', 'V', 'F', 'A']
We can make the for loop more compact at the expense of readability. We put our destination lists into a tuple and then use the equality test to select which list in that tuple to append to. This works because False has a numeric value of 0, and True has a numeric value of 1.
for x, y in zip(small, large):
(different, identicals)[x == y].append(y)
The problem is the inner loop. You are comparing each of the letters in list1 with all the letters of list2.
Instead you should have a single loop:
identicals=[]
different=[]
short_list = list1 if len(list1)<= len(list2) else list2
for i in range(len(short_list):
if list1[i] == list2[i]:
identicals.append(list1[i])
else:
different.append(short_list[i])
Try this
a = "ABCDEFABCDEF"
b = "AZBYCXDWEVFABCDEF"
import numpy
A = numpy.array(list(a))
B = numpy.array(list(b))
common = A[:len(B)] [ (A[:len(B)] == B[:len(A)]) ]
different = A[:len(B)] [ - (A[:len(B)] == B[:len(A)]) ]
>>> list(common)
['A']
>>> list(different)
['B', 'C', 'D', 'E', 'F', 'A', 'B', 'C', 'D', 'E', 'F']
I have two lists (of different lengths). One changes throughout the program (list1), the other (longer) doesn't (list2). Basically I have a function that is supposed to compare the elements in both lists, and if an element in list1 is in list2, that element in a copy of list2 is changed to 'A', and all other elements in the copy are changed to 'B'. I can get it to work when there is only one element in list1. But for some reason if the list is longer, all the elements in list2 turn to B....
def newList(list1,list2):
newList= list2[:]
for i in range(len(list2)):
for element in list1:
if element==newList[i]:
newList[i]='A'
else:
newList[i]='B'
return newList
Try this:
newlist = ['A' if x in list1 else 'B' for x in list2]
Works for the following example, I hope I understood you correctly? If a value in B exists in A, insert 'A' otherwise insert 'B' into a new list?
>>> a = [1,2,3,4,5]
>>> b = [1,3,4,6]
>>> ['A' if x in a else 'B' for x in b]
['A', 'A', 'A', 'B']
It could be because instead of
newList: list2[:]
you should have
newList = list2[:]
Personally, I prefer the following syntax, which I find to be more explicit:
import copy
newList = copy.copy(list2) # or copy.deepcopy
Now, I'd imagine part of the problem here is also that you use the same name, newList, for both your function and a local variable. That's not so good.
def newList(changing_list, static_list):
temporary_list = static_list[:]
for index, content in enumerate(temporary_list):
if content in changing_list:
temporary_list[index] = 'A'
else:
temporary_list[index] = 'B'
return temporary_list
Note here that you have not made it clear what to do when there are multiple entries in list1 and list2 that match. My code marks all of the matching ones 'A'. Example:
>>> a = [1, 2, 3]
>>> b = [3,4,7,2,6,8,9,1]
>>> newList(a,b)
['A', 'B', 'B', 'A', 'B', 'B', 'B', 'A']
I think this is what you want to do and can put newLis = list2[:] instead of the below but prefer to use list in these cases:
def newList1(list1,list2):
newLis = list(list2)
for i in range(len(list2)):
if newLis[i] in list1:
newLis[i]='A'
else: newLis[i]='B'
return newLis
The answer when passed
newList1(range(5),range(10))
is:
['A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B']