Iterating over 2 lists at once and comparing the elements - python

This is just a small part of my homework assignment. What im trying to do is iterate through list1 and iterate backwards through list2 and determine if list2 is a reversed version of list1. Both lists are equal length.
example: list1 = [1,2,3,4] and list2 = [4,3,2,1]. list2 is a reversed version of list1. You could also have list1 = [1,2,1] and list2 = [1,2,1] then they would be the same list and also reversed lists.
Im not asking for exact code, im just not sure how i would code this. Would i run 2 loops? Any tips are appreciated. Just looking for a basic structure/algorithm.
edit: we are not allowed to use any auxiliary lists etc.

You can just iterate backwards on the second list, and keep a counter of items from the start of the first list. If items match, break out of the loop, otherwise keep going.
Here's what it can look like:
def is_reversed(l1, l2):
first = 0
for i in range(len(l2)-1, -1, -1):
if l2[i] != l1[first]:
return False
first += 1
return True
Which Outputs:
>>> is_reversed([1,2,3,4], [4,3,2,1])
True
>>> is_reversed([1,2,3,4], [4,3,2,2])
False
Although it would be easier to just use builtin functions to do this shown in the comments.

The idea here is that whenever you have an element list1[i], you want to compare it to the element list2[-i-1]. As required, the following code creates no auxiliary list in the process.
list1 = [1, 2, 3]
list2 = [3, 2, 1]
are_reversed = True
for i in range(len(list1)):
if list1[i] != list2[-i - 1]:
are_reversed = False
break
I want to point out that the built-in range does not create a new list in Python3, but a range object instead. Although, if you really want to stay away from those as well you can modify the code to use a while-loop.
You can also make this more compact by taking advantage of the built-in function all. The following line instantiate an iterator, so this solution does not create an auxiliary list either.
are_reversed = all(list1[i] == list2[-i - 1] for i in range(len(list2))) # True

If you want to get the N'th value of each list you can do a for loop with
if (len(list1) <= len(list2):
for x in range(0, len(list1):
if (list1[x] == list2[x]):
#Do something
else:
for x in range(0, len(list2):
if (list1[x] == list2[x]):
#Do something
If you want to check if each value of a list with every value of another list you can nestle a for loop
for i in list1:
for j in list2:
if (list1[i] == list2[j]):
//Do something
EDIT: Changed code to Python

Related

searching a list for duplicate values in python 2.7

list1 = ["green","red","yellow","purple","Green","blue","blue"]
so I got a list, I want to loop through the list and see if the colour has not been mentioned more than once. If it has it doesn't get append to the new list.
so u should be left with
list2 = ["red","yellow","purple"]
So i've tried this
list1 = ["green","red","yellow","purple","Green","blue","blue","yellow"]
list2 =[]
num = 0
for i in list1:
if (list1[num]).lower == (list1[i]).lower:
num +=1
else:
list2.append(i)
num +=1
but i keep getting an error
Use a Counter: https://docs.python.org/2/library/collections.html#collections.Counter.
from collections import Counter
list1 = ["green","red","yellow","purple","green","blue","blue","yellow"]
list1_counter = Counter([x.lower() for x in list1])
list2 = [x for x in list1 if list1_counter[x.lower()] == 1]
Note that your example is wrong because yellow is present twice.
The first problem is that for i in list1: iterates over the elements in the list, not indices, so with i you have an element in your hands.
Next there is num which is an index, but you seem to increment it rather the wrong way.
I would suggest that you use the following code:
for i in range(len(list1)):
unique = True
for j in range(len(list1)):
if i != j and list1[i].lower() == list1[j].lower():
unique = False
break
if unique:
list2.append(list1[i])
How does this work: here i and j are indices, you iterate over the list and with i you iterate over the indices of element you potentially want to add, now you do a test: you check if somewhere in the list you see another element that is equal. If so, you set unique to False and do it for the next element, otherwise you add.
You can also use a for-else construct like #YevhenKuzmovych says:
for i in range(len(list1)):
for j in range(len(list1)):
if i != j and list1[i].lower() == list1[j].lower():
break
else:
list2.append(list1[i])
You can make the code more elegant by using an any:
for i in range(len(list1)):
if not any(i != j and list1[i].lower() == list1[j].lower() for j in range(len(list1))):
list2.append(list1[i])
Now this is more elegant but still not very efficient. For more efficiency, you can use a Counter:
from collections import Counter
ctr = Counter(x.lower() for x in list1)
Once you have constructed the counter, you look up the amount of times you have seen the element and if it is less than 2, you add it to the list:
from collections import Counter
ctr = Counter(x.lower() for x in list1)
for element in list1:
if ctr[element.lower()] < 2:
list2.append(element)
Finally you can now even use list comprehension to make it very elegantly:
from collections import Counter
ctr = Counter(x.lower() for x in list1)
list2 = [element for element in list1 if ctr[element.lower()] < 2]
Here's another solution:
list2 = []
for i, element in enumerate(list1):
if element.lower() not in [e.lower() for e in list1[:i] + list1[i + 1:]]:
list2.append(element)
By combining the built-ins, you can keep only unique items and preserve order of appearance, with just a single empty class definition and a one-liner:
from future_builtins import map # Only on Python 2 to get generator based map
from collections import Counter, OrderedDict
class OrderedCounter(Counter, OrderedDict):
pass
list1 = ["green","red","yellow","purple","Green","blue","blue"]
# On Python 2, use .iteritems() to avoid temporary list
list2 = [x for x, cnt in OrderedCounter(map(str.lower, list1)).items() if cnt == 1]
# Result: ['red', 'yellow', 'purple']
You use the Counter feature to count an iterable, while inheriting from OrderedDict preserves key order. All you have to do is filter the results to check and strip the counts, reducing your code complexity significantly.
It also reduces the work to one pass of the original list, with a second pass that does work proportional to the number of unique items in the list, rather than having to perform two passes of the original list (important if duplicates are common and the list is huge).
Here's yet another solution...
Firstly, before I get started, I think there is a typo... You have "yellow" listed twice and you specified you'd have yellow in the end. So, to that, I will provide two scripts, one that allows the duplicates and one that doesn't.
Original:
list1 = ["green","red","yellow","purple","Green","blue","blue","yellow"]
list2 =[]
num = 0
for i in list1:
if (list1[num]).lower == (list1[i]).lower:
num +=1
else:
list2.append(i)
num +=1
Modified (does allow duplicates):
colorlist_1 = ["green","red","yellow","purple","Green","blue","blue","yellow"]
colorlist_2 = []
seek = set()
i = 0
numberOfColors = len(colorlist_1)
while i < numberOfColors:
if numberOfColors[i].lower() not in seek:
seek.add(numberOfColors[i].lower())
colorlist_2.append(numberOfColors[i].lower())
i+=1
print(colorlist_2)
# prints ["green","red","yellow","purple","blue"]
Modified (does not allow duplicates):
EDIT
Willem Van Onsem's answer is totally applicable and already thorough.

How to check if two lists of tuples are identical

I need to check to see if a list of tuples is sorted, by the first attribute of the tuple. Initially, I thaught to check this list against its sorted self. such as...
list1 = [(1, 2), (4, 6), (3, 10)]
sortedlist1 = sorted(list1, reverse=True)
How can I then check to see if list1 is identical to sortedlist1? Identical, as in list1[0] == sortedlist1[0], and list1[1] == sortedlist1[1].
The list may have a length of 5 or possibly 100, so carrying out list1[0] == sortedlist1[0], and list1[1] == sortedlist1[1] would not be an option because I am not sure how long the list is.
Thanks
I believe you can just do list1 == sortedlist1, without having to look into each element individually.
#joce already provided an excellent answer (and I would suggest accepting that one as it is more concise and directly answers your question), but I wanted to address this portion of your original post:
The list may have a length of 5 or possibly 100, so carrying out list1[0] == sortedlist1[0], and list1[1] == sortedlist1[1] would not be an option because I am not sure how long the list is.
If you want to compare every element of two lists, you do not need to know exactly how long the lists are. Programming is all about being lazy, so you can bet no good programmer would write out that many comparisons by hand!
Instead, we can iterate through both lists with an index. This will allow us to perform operations on each element of the two lists simultaneously. Here's an example:
def compare_lists(list1, list2):
# Let's initialize our index to the first element
# in any list: element #0.
i = 0
# And now we walk through the lists. We have to be
# careful that we do not walk outside the lists,
# though...
while i < len(list1) and i < len(list2):
if list1[i] != list2[i]:
# If any two elements are not equal, say so.
return False
# We made it all the way through at least one list.
# However, they may have been different lengths. We
# should check that the index is at the end of both
# lists.
if i != (len(list1) - 1) or i != (len(list2) - 2):
# The index is not at the end of one of the lists.
return False
# At this point we know two things:
# 1. Each element we compared was equal.
# 2. The index is at the end of both lists.
# Therefore, we compared every element of both lists
# and they were equal. So we can safely say the lists
# are in fact equal.
return True
That said, this is such a common thing to check for that Python has this functionality built in through the quality operator, ==. So it's much easier to simply write:
list1 == list2
If you want to check if a list is sorted or not a very simple solution comes to mind:
last_elem, is_sorted = None, True
for elem in mylist:
if last_elem is not None:
if elem[0] < last_elem[0]:
is_sorted = False
break
last_elem = elem
This has the added advantage of only going over your list once. If you sort it and then compare it, you're going over the list at least greater than once.
If you still want to do it that way, here's another method:
list1 = [(1, 2), (4, 6), (3, 10)]
sortedlist1 = sorted(list1, reverse=True)
all_equal = all(i[0] == j[0] for i, j in zip(list1, sortedlist1))
In python 3.x, you can check if two lists of tuples
a and b are equal using the eq operator
import operator
a = [(1,2),(3,4)]
b = [(3,4),(1,2)]
# convert both lists to sets before calling the eq function
print(operator.eq(set(a),set(b))) #True
Use this:
sorted(list1) == sorted(list2)

Creating a function that removes duplicates in list

I'm trying to manually make a function that removes duplicates from a list. I know there is a Python function that does something similar (set()), but I want to create my own. This is what I have:
def remove(lst):
for i in range(len(lst)):
aux = lst[0:i] + lst[i+1:len(lst)]
if lst[i] in aux:
del(lst[i])
return lst
I was trying something like creating a sub-list with all the items except the one the for is currently on, and then check if the item is still in the list. If it is, remove it.
The problem is that it gives me an index out of range error. Does the for i in range(len(lst)): line not update every time it starts over? Since I'm removing items from the list, the list will be shorter, so for a list that has 10 items and 2 duplicates, it will go up to index 9 instead of stopping on the 7th.
Is there anyway to fix this, or should I just try doing this is another way?
I know this does not fix your current script, but would something like this work?
def remove(lst):
unique=[]
for i in lst:
if i not in unique: unique.append(i)
return unique
Just simply looping through, creating another list and checking for membership?
The problem is you are manipulating the list as you are iterating over it. This means that when you reach the end of the list, it is now shorter because you're removed elements. You should (generally) avoid removing elements while you are looping over lists.
You got it the first time: len(lst) is evaluated only when you enter the loop. If you want it re-evaluated, try the while version:
i = 0
while i < len(lst):
...
i += 1
Next, you get to worry about another problem: you increment i only when you don't delete an item. When you do delete, shortening the list gets you to the next element.
i = 0
while i < len(lst):
aux = lst[0:i] + lst[i+1:len(lst)]
if lst[i] in aux:
del(lst[i])
else:
i += 1
I think that should solve your problem ... using the logic you intended.
def remove(lst):
new_list = []
for i in lst:
if i not in new_list:
new_list.append(i)
return new_list
You should append the values to a secondary list. As Bobbyrogers said, it's not a good idea to iterate over a list that is changing.
You can also try this:
lst = [1,2,3,3,4,4,5,6]
lst2 = []
for i in lst:
if i not in lst2:
lst2.append(i)
print(lst2)
[1, 2, 3, 4, 5, 6]

list index out of range

I can't figure out why this is throwing up a "List index out of range error". This code is meant to remove duplicates from a list.
def remove_duplicates(l):
new_list = l
for i in range(0,len(l)):
store = l[i]
for x in range(i+1,len(l)):
if l[x] == store:
new_list.pop(x)
return new_list
print remove_duplicates([1,1,2,2])
Thanks for all the answers. So i tried this (after considerable brain-wracking ) and i can't figure out what's wrong this time.
def remove_duplicates(l):
new_list = l[:]
for i in range(0,len(l)):
count = 0
store = l[i]
for x in range(0,len(new_list)):
if l[x] == store:
count += 1
if count >= 2:
new_list.remove(l[i])
return new_list
print remove_duplicates([1,1,2,2])
Which print [2,2] to the console. I used the remove function, so it can't be an indexing error. I don't see how it can remove the 1 on the second iteration. I'm looping over the modified list in the second for loop, there's no way the count can be >= 2 in the if condition.
new_list = l makes new_list refer to the same object as l. Any changes to new_list will then affect l, which causes errors in your code. You get the out-of-range error because you remove items from new_list, which actually removes them from l, while you're iterating over l.
Use new_list = l[:] to copy l to a new list.
You also have errors with the logic of your code: new_list.pop(x) will change the indexes of all the following elements, which means the next time you remove an element, it will be removed from the wrong index.
You are not copying the list properly, your new_list refers to the same memory location i.e. object as l hence any change you make in new_list will reflect in l as well.
To avoid this you can use shallow copy
example:-
new_list=l[:]
List comprehensions tend to be much faster than for loops and look fancy to boot. if your intention is to create a new list with duplicates removed, this will do it:
def remove_duplicates(mylist):
return [elem for i, elem in enumerate(mylist) if elem not in mylist[i+1:]]
print remove_duplicates([1,1,2,2])
If you have a very large list with many duplicates, a for loop with a set() to track what you've seen may be faster.

Indices are not synchronous of using one lists indices in another

Coming from R, this was hard to grasp. Taking elements from a list start with position 0.
The problem is that using one list to select items from another list are not running at the same pace here.
list1 = [1,2,3,4]
list2 = [1,2,3,4]
for x in range(0, len(list1)):
print(list1[list2[x]])
This will result in:
>> 2
>> 3
>> 4
>> IndexError: list index out of range
When I put an extra item in the start of list1, and added an item at the end of list2, the problem stops (simply because they are not synchronous like this).
Obviously I am not familiar with the language yet, what would be the correct way to use values from one list to select values from another?
Is this the correct way to think of it?
for x in range(0, len(list1)):
print(list1[list2[x]-1])
Python is 0-index based. seq[0] is the first element in seq.
R is 1-index based.
So, yes, in python you could use
list1 = [1,2,3,4]
list2 = [1,2,3,4]
for x in range(0, len(list2)):
print(list1[list2[x]-1])
The range should go up to len(list2), not len(list1).
Also, range(0, len(list2)) is the same as range(len(list2)). When
range is passed only one argument, it is interpreted as the stop
value, with a start value of 0 taken by default.
Note that in Python
for x in range(...):
can often be avoided, and if so, is preferable. Instead, you can write
for item in list2:
print(list1[item-1])
and item will be assigned to each of the items in list2.
If your list has 4 items, your indexes must run from 0 to 3, so using the value 4 throws an error. Here's an example with letters which might make it clearer:
list1 = [0,2,1,3]
list2 = ['a','a','d','m']
for x in list1:
print(list2[x]),
=> a d a m

Categories