This question already has answers here:
Python removing overlap of lists
(2 answers)
Closed 2 years ago.
I have two lists in Python, like these:
temp1 = ['A', 'A', 'A', 'B', 'C', 'C','C']
temp2 = ['A','B','C','C']
I need to create a third list with items from the first list which will be different with exact number of elements existing in temp2, I need to create below :
temp3 = ['A','A','C']
What is the best way of doing that ? Using sets is not working as expected, so that would like to now is there a fast way to do it with python standart functions or i have to create my own function ?
temp1 = ['A', 'A', 'A', 'B', 'C', 'C','C']
temp2 = ['A','B','C','C']
# create a copy of your first list
temp3 = list(temp1)
# remove every item from the second list of the copy
for e in temp2:
temp3.remove(e)
Output:
['A', 'A', 'C']
If the lists are guaranteed to be sorted you can do much better in terms of time complexity than list.remove or counting every iteration using:
temp1 = ['A', 'A', 'A', 'B', 'C', 'C', 'C']
temp2 = ['A', 'B', 'C', 'C']
filtered = []
j = 0
for i, letter in enumerate(temp1):
while j < len(temp2) and temp2[j] < letter:
j += 1
if j == len(temp2):
break
if temp2[j] > letter:
filtered.append(letter)
else:
j += 1
filtered.extend(temp1[i:])
Another solution
A more interesting solution I thought of:
from collections import Counter
result = []
for letter, count in (Counter(temp1)-Counter(temp2)).items():
result.extend([letter]*count)
This is the same big O complexity as the above.
If lists are not sorted
If order is not important these solutions are still much faster, since sorting the lists is cheaper than the O(n^2) solutions, and the second one doesn't even need that. If it is, this still works, you just need to retain a mapping of element->index (which your temp1 already is) before sorting, though this might be out of scope for this question.
from collections import Counter
temp1 = ['A', 'A', 'A', 'B', 'C', 'C', 'C']
temp2 = ['A', 'B', 'C', 'C']
result = []
counts = Counter(temp2)
for item in temp1:
if item in counts and counts[item]:
counts[item] -= 1
else:
result.append(item)
print(result)
Output:
['A', 'A', 'C']
Scales O(n) and does not rely on sorted input.
This answer relies on the fact that Counter is just a subclass of dict, so we can use the instance as a mutable object in which to store the number of occurrences in temp2 that we still need to exclude from the result during the iteration over temp1. The documentation states explicitly that "Counter is a dict subclass" and that "Counter objects have a dictionary interface", which is a pretty good guarantee that item assignment will be supported, and that it is not necessary to treat it as a read-only object that must first be copied into a plain dict.
You can try
temp1 = ['A', 'A', 'A', 'B', 'C', 'C','C']
temp2 = ['A','B','C','C']
temp3 = []
for i in temp1:
if temp1.count(i) - temp2.count(i) > temp3.count(i):
temp3.append(i)
print(temp3)
This code will check if in temp3 all the diff elements init and if not it will append the relevant temp1 item to the temp3 list.
Output
['A', 'A', 'C']
Related
n = int(input())
string = input()
string_list = list(string)
division_list = []
for i in range(n):
if n%(i+1) == 0:
division_list.append(i+1)
division_list.sort(reverse=True)
print(division_list)
for division in division_list:
temp1 = string_list[:division]
temp2 = string_list[division:]
print('before reverse')
print(temp1)
temp1.sort(reverse=True)
print('after reverse')
print(temp1)
string_list = temp1 + temp2
ans = ''.join(string_list)
print(ans)
I was solving problem in code forces, and realized sort function doesn't work more than one in for loop. Why? And is there any further stuffs I can do on my code?
This is the link of the question.
https://codeforces.com/problemset/problem/999/B
edited)
I put 4 and abcd as a input. This is the output. I expected to see ['c', 'd'] after second temp1.sort(reverse=True). But it doesn't reversed.
4
abcd
[4, 2, 1]
before reverse
['a', 'b', 'c', 'd']
after reverse
['d', 'c', 'b', 'a']
before reverse
['d', 'c']
after reverse
['d', 'c']
before reverse
['d']
after reverse
['d']
dcba
Python .sort function sorts the list in ascending order. What the reverse parameter does is reverses the sorted list; so in this case in descending order, which happens to be the original list.
If you are trying to reverse the list I think temp1.reverse() would work (https://www.geeksforgeeks.org/python-list-reverse/). Otherwise, you could do something like this:
temp1 = temp1[::-1]
This question already has answers here:
Wrapping around on a list when list index is out of range
(3 answers)
Closed 5 years ago.
Title may be confusing but I don't know how to express myself any better. What I have is a list that looks something like this:
myList = ['a', 'b', 'c', 'd', 'e', 'f']
what I want to do is a for loop that, for example, starts at index 3, so 'd' and then goes to the end but at the end of the list insted of finishing goes back to the beginning, goes through 'a','b','c' and then finishes at 'd'. I tried doing it like this with a while loop:
index = 3
while index != 2:
if index == len(a):
index = 0
else:
pass
print(a[index])
index += 1
This sorta worked but it will never print out 'c' but if I put 3 as index the loop will never even start. Is there a more elegant solution to do something like this?
You can use the modulus operator against the length of the string
def offset_print(s, offset):
for i in range(len(s)):
print(s[(i+offset) % len(s)])
Example
>>> offset_print('hello', 2)
l
l
o
h
e
So, definitely like #CoryKramer's approach, but this one doesn't require you to calculate the length of the iterator:
def run_offset(lst, fn, offset=0):
base = []
for item in lst:
if offset > 0:
# append the original list until you're no longer dealing with an offset
base.append(item)
offset -= 1
continue
# just yield the current value
yield item
# iterate through the indexes 0... offset
for item in base:
yield item
> list(run_offset('abcd', print, 2))
['c', 'd', 'a', 'b']
Not the most efficient -- but if your list is short and you just want pure simplicity, you can shift your list;
shifted = my_list[n:] + my_list[:n]
for item in shifted:
# do stuff here
... Or use a deque;
>>> import collections
>>> dq = collections.deque(['a', 'b', 'c', 'd'])
>>> dq
deque(['a', 'b', 'c', 'd'])
>>> dq.rotate(2)
>>> dq
deque(['c', 'd', 'a', 'b'])
Using itertools.cycle also works here:
from itertools import cycle
def offset_print(s, offset):
it = cycle(s)
items = [next(it) for i in range(len(s) + offset)]
for x in items[offset:]:
print(x)
Which Outputs:
>>> offset_print(['a', 'b', 'c', 'd', 'e', 'f'], 2)
c
d
e
f
a
b
answer_list = ['a', 'b', 'c', 'd']
student_answers = ['a', 'c', 'b', 'd']
incorrect = []
I want to compare index 0 in list1 to index 0 in list2 and, if they are equal, move to compare index 1 in each list.
In this instance index 1 in list1 != index 1 in list 2 so I want to append index+1 and the incorrect student answer (in this case the letter c) to the empty list. This is what I tried - unsuccessfully.
def main():
list1 = ['a', 'b', 'c', 'd']
list2 = ['a', 'c', 'b', 'd']
incorrect = []
for x in list1:
for y in list2:
if x != y:
incorrect.append(y)
print(incorrect)
main()
Since you need to compare lists element-by-element, you also need to iterate over those list simultaneously. There is more than one way to do this, here are a few.
Built-in function zip allows you to iterate over multiple iterable objects. This would be my method of choice because, in my opinion, it's the easiest and the most readable way to iterate over several sequences all at once.
for x,y in zip(list1, list2):
if x != y:
incorrect.append(y)
The other way would be to use method enumerate:
for pos, value in enumerate(list1):
if value != list2[pos]:
incorrect.append(list2[pos])
Enumerate takes care of keeping track of indexing for you, so you don't need to create a special counter just for that.
The third way is to iterate over lists using index. One way to do this is to write:
for pos range(len(list1)):
if list1[pos] != list2[pos]:
incorrect.append(list2[pos])
Notice how by using enumerate you can get index out-of-the-box.
All of those methods can also be written using list comprehensions, but in my opinion, this is more readable.
You can use enumerate and list comprehension to check the index comparison.
answer_list = ['a', 'b', 'c', 'd']
student_answers = ['a', 'c', 'b', 'd']
incorrect = [y for x,y in enumerate(answer_list) if y != student_answers[x]]
incorrect
['b', 'c']
If you want the indexes that don't match and the values:
incorrect = [[y,answer_list.index(y)] for x,y in enumerate(answer_list) if y != student_answers[x]]
[['b', 1], ['c', 2]]
In x,y in enumerate(answer_list), the x is the index of the element and y is the element itself, so checking if y != student_answers[x] is comparing the elements at the same index in both lists. If they don't match, the element y is added to our list.
Using a loop similar to your own:
def main():
list1 = ['a', 'b', 'c', 'd']
list2 = ['a', 'c', 'b', 'd']
incorrect = []
for x,y in enumerate(list1):
if list2[x] != y:
incorrect.append(y)
print(incorrect)
In [20]: main()
['b', 'c']
To get element and index:
def main():
list1 = ['a', 'b', 'c', 'd']
list2 = ['a', 'c', 'b', 'd']
incorrect = []
for x,y in enumerate(list1):
if list2[x] != y:
incorrect.append([y,list1.index(y)])
print(incorrect)
In [2]: main()
[['b', 1], ['c', 2]]
I need to write a program in Python that compares two parallel lists to grade a multiple choice exam. One list has the exam solution and the second list has a student's answers. The question number for each missed question is to be stored in a third list using the natural index numbers. The solution must use indexing.
I keep getting an empty list returned for the third list. All help much appreciated!
def main():
exam_solution = ['B', 'D', 'A', 'A', 'C', 'A', 'B', 'A', 'C', 'D', 'B', 'C',\
'D', 'A', 'D', 'C', 'C', 'B', 'D', 'A']
student_answers = ['B', 'D', 'B', 'A', 'C', 'A', 'A', 'A', 'C', 'D', 'B', 'C',\
'D', 'B', 'D', 'C', 'C', 'B', 'D', 'A']
questions_missed = []
for item in exam_solution:
if item not in student_answers:
questions_missed.append(item)
questions_missed = [i for i, (ex,st) in enumerate(zip(exam_solution, student_answers)) if ex != st]
or alternatively, if you prefer loops over list comprehensions:
questions_missed = []
for i, (ex,st) in enumerate(zip(exam_solution, student_answers)):
if ex != st:
questions_missed.append(i)
Both give [2,6,13]
Explanation:
enumerate is a utility function that returns an iterable object which yields tuples of indices and values, it can be used to, loosely speaking, "have the current index available during an iteration".
Zip creates a list of tuples, containing corresponding elements from two or more iterable objects (in your case lists).
I'd prefer the list comprehension version.
If I add some timing code, I see that performance doesn't really differ here:
def list_comprehension_version():
questions_missed = [i for i, (ex,st) in enumerate(zip(exam_solution, student_answers)) if ex != st]
return questions_missed
def loop_version():
questions_missed = []
for i, (ex,st) in enumerate(zip(exam_solution, student_answers)):
if ex != st:
questions_missed.append(i)
return questions_missed
import timeit
print "list comprehension:", timeit.timeit("list_comprehension_version", "from __main__ import exam_solution, student_answers, list_comprehension_version", number=10000000)
print "loop:", timeit.timeit("loop_version", "from __main__ import exam_solution, student_answers, loop_version", number=10000000)
gives:
list comprehension: 0.895029446804
loop: 0.877159359719
A solution based on iterators
questions_missed = list(index for (index, _)
in filter(
lambda (_, (answer, solution)): answer != solution,
enumerate(zip(student_answers, exam_solution))))
For the purists, note that you should import the equivalents of zip and filter (izip and ifilter) from itertools.
One more solution comes to my mind. I put in in a separate answers as it is "special"
Using numpy this task can be accomplished by:
import numpy as np
exam_solution = np.array(exam_solution)
student_answers = np.array(student_answers)
(exam_solution!=student_answers).nonzero()[0]
With numpy-arrays, elementwise comparison is possible via == and !=. .nonzero() returns the indices of the array elements that are not zero. That's it.
Timing is really interesting now. For your 19-elements lists, performances are (N=19,repetitions=100,000):
list comprehension: 0.904024521544
loop: 0.936516107421
numpy: 0.349371968612
This is already a factor of almost 3. Nice, but not amazing.
But when I increase the size of your lists by a factor of 100, I get (N=19*100=1900, repetitions=1000):
list comprehension: 0.866544042939
loop: 1.04464069977
numpy: 0.0334220694495
Now we have a factor of 26 or 31 - that is definitely a lot.
Probably, performance won't be your problem, but, nevertheless, I thought it's worth pointing out.
Given input:
list = [['a']['a', 'c']['d']]
Expected Ouput:
mylist = a,c,d
Tried various possible ways, but the error recieved is TypeError: list indices must be integers not tuple.
Tried:
1.
k= []
list = [['a']['a', 'c']['d']]
#k=str(list)
for item in list:
k+=item
print k
2.
print zip(*list)
etc.
Also to strip the opening and closing parenthesis.
What you want is flattening a list.
>>> import itertools
>>> l
[['a'], ['a', 'c'], ['d']]
>>> res = list(itertools.chain.from_iterable(l))
>>> res
['a', 'a', 'c', 'd']
>>> set(res) #for uniqify, but doesn't preserve order
{'a', 'c', 'd'}
Edit: And your problem is, when defining a list, you should seperate values with a comma. So, not:
list = [['a']['a', 'c']['d']]
Use commas:
list = [['a'], ['a', 'c'], ['d']]
And also, using list as a variable is a bad idea, it conflicts with builtin list type.
And, if you want to use a for loop:
l = [['a'], ['a', 'c'], ['d']]
k = []
for sublist in l:
for item in sublist:
if item not in k: #if you want list to be unique.
k.append(item)
But using itertools.chain is better idea and more pythonic I think.
While utdemir's answer does the job efficiently, I think you should read this - start from "11.6. Recursion".
The first examples deals with a similar problem, so you'll see how to deal with these kinds of problems using the basic tools.