Non-unique Elements - python

I am doing an exercise on checkio about writing a function to exclude the unique elements in a list, and only keep the non-unique ones. I wrote the following lines.
def checkio(data):
for i in data:
if data.count(i) == 1:
data.remove(i)
return data
And then did the following tests.
checkio([1, 2, 3, 1, 3]) == [1, 3, 1, 3]
checkio([1, 2, 3, 4, 5]) == []
checkio([5, 5, 5, 5, 5]) == [5, 5, 5, 5, 5]
checkio([10, 9, 10, 10, 9, 8]) == [10, 9, 10, 10, 9]
But the second test won't pass. Instead of returning an empty array [], it returns [2,4].
Why is that?
Thanks for the help in advance!

Solution:
def checkio(data):
duplicates = []
for i in data:
if data.count(i) > 1:
duplicates.append(i)
return duplicates
Here, as mentioned by #sp4c38 instead of removing the unique elements and modifying the original list, add the duplicates to a new list.
This passes all the test-cases:
checkio([1, 2, 3, 1, 3])
checkio([1, 2, 3, 4, 5]) == []
checkio([5, 5, 5, 5, 5]) == [5, 5, 5, 5, 5]
checkio([10, 9, 10, 10, 9, 8]) == [10, 9, 10, 10, 9]

the code traps it self. The problem is that the for in data:... takes the data[0] (the first item) of your list. 1 is unique, so your program removes the 1 from your data list. data is now: [2,3,4,5]. Now the for-in loop goes on with data[1] which is now the number 3. 3 is also unique so it removes it. Your list is now: data = [2,4,5]. Now your code goes on with data[2], which is 5. It's removed. At the end there is data = [2,4] left. Try to find another solution for your problem. Hope I could help you.
Edit:
You shouldn't remove the items from the list. Try to create a new list where you put all non-unique items in it. Try to use "smaller than" and "bigger than" relations (<,>).

You can accomplish that using a single liner
def checkio(data):
return [x for x in data if data.count(x) > 1]

Related

cycle through a list while skipping elements (python)

I'm trying to find a way of going through a loop of a single list skipping the first element each time.
so for example, if I have a list:
lst = [0,1,2,3,4,5,6]
and wish to use a for loop to cycle through all but the first element and stop at an arbitrary point. So if I wish to start at index 1 of lst and advance through a cycle of each element (excluding the first) I can use the following:
scd = [1,2,3,4,5,6]
out = []
for i in range (1,14+1):
if (i%len(lst)) not in scd:
continue
else:
out.append(lst[i%len(lst)])
print(out)
it returns a list with only 12 elements:
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
The output that I'm trying to get is:
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2]
I know the issue is in using the continue in the for loop, but I'm struggling with a workaround that gets me the desired output.
Any help would be greatly appreciated. also if there's a more pythonic way of going about this problem, I'm all ears.
Thanks so much.
IIUC, you simply want to repeat the same list, right? How about this where you could concatenate lists using divmod:
num = 14
i,j = divmod(num, len(lst)-1)
out = lst[1:] * i + lst[1:1+j]
Output:
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2]

problems in my list sorting code in python

I've come up with this code to sort out duplicates in a randomly arranged list of numbers.
counter = 0
randomDup_list = [0, 3, 0, 1, 9, 8, 2, 3, 4, 2, 4, 3, 5, 6, 0, 6, 5, 2, 6, 6, 7, 8, 9, 4, 4]
dup_sorted = []
for x in randomDup_list:
if len(randomDup_list) == 0:
dup_sorted.append(x)
counter +=1
elif x != randomDup_list[counter]:
for y in dup_sorted:
if y != x:
dup_sorted.append(x)
print(dup_sorted)
When I run the code there are no errors but the numbers don't seem to be appending to my new list, and it comes out blank like this: [].
The most pythonic way to do this is with a list comprehension, like so:
dup_sorted = [el for index, el in enumerate(randomDup_list) if el not in randomDup_list[:index]]
Enumerate will create a list of tuples with the first tuple element as the index in the list, [(0,0), (1,3), (2,0), ...] are the first 3 elements in your case.
Then it basically checks if el is the first occurence of el in the list and if it is, it adds el to the dup_sorted list.
List comprehensions are maybe hard to understand, but there is plenty of information about them on the internet. Good luck with learning Python!
I did not understand what you want but you can basically sort the list like that
print(sorted(randomDup_list))
and you can create your new list like that
dup_sorted = sorted(randomDup_list)
print(dup_sorted)
Hey welcome to learning python. I have been coding for a couple of years but I still use print to test my logic.
Lets break down your code and add print to each logic test
counter = 0
randomDup_list = [0, 3, 0, 1, 9, 8, 2, 3, 4, 2, 4, 3, 5, 6, 0, 6, 5, 2, 6, 6, 7, 8, 9, 4, 4]
dup_sorted = []
for x in randomDup_list:
print("start iteration")
if len(randomDup_list) == 0:
print("1 true")
dup_sorted.append(x)
counter +=1
elif x != randomDup_list[counter]:
print("2 true")
print(dup_sorted)
for y in dup_sorted:
if y != x:
print("3 true")
dup_sorted.append(x)
print("stop iteration")
print(dup_sorted)
If you run this code you will see that the first logic test will never be true in your example. So it will go to the second logic test. This will eventually evaluate to true but there will be some iterations that will be skipped. Blank blocks of start iteration stop iteration. For the last logic test this will never be true because dup_sorted will always be empty. So for y in dup_sorted will result to nothing.
Also I think sort out is ambiguous. Is it sort? filter?
You can do set(randomDup_list) for filtering out duplicates
You can do sorted(randomDup_list) for sorting out the list
You can do sorted(list(set(randomDup_list)) for a sorted filtered list
Seeing your new comments
randomDup_list = [0, 3, 0, 1, 9, 8, 2, 3, 4, 2, 4, 3, 5, 6, 0, 6, 5, 2, 6, 6, 7, 8, 9, 4, 4]
dup_sorted = []
for x in randomDup_list:
if x in dup_sorted:
continue
else:
dup_sorted.append(x)
print(dup_sorted)
This initializes an empty list. Loops through your list. Check if it exists then appends if not. Since List maintain order, you can expect the order not to change.
To remove any duplicates from the list without changing the order
Code
dup_sorted = []
for x in randomDup_list:
if not x in dup_sorted: # check if x is in output yet
dup_sorted.append(x) # add x to output
print(dup_sorted)
# Output: [0, 3, 1, 9, 8, 2, 4, 5, 6, 7]
You can use this:
counter = 0
randomDup_list = [0, 3, 0, 1, 9, 8, 2, 3, 4, 2, 4, 3, 5, 6, 0, 6, 5, 2, 6, 6, 7, 8, 9, 4, 4]
dup_sorted = []
dup_sorted.append(randomDup_list[0])
for x in range(len(randomDup_list)):
temp = 0
for y in range(len(dup_sorted)):
if randomDup_list[x] != dup_sorted[y]:
temp = temp + 1
if temp == len(dup_sorted):
dup_sorted.append(randomDup_list[x])
print(dup_sorted)

Python move all elements of a list one position back with same len

def list_move_back(new_value, value_list):
for i in reversed(value_list):
if value_list.index(i) != len(value_list)-1:
value_list[value_list.index(i)+1] = i
value_list[0] = new_value
return value_list
I want to get the following result:
list_example = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list_example = list_move_back(12, list_example]
print(list_example)
>>>[12, 1, 2, 3, 4, 5, 6, 7, 8, 9]
It works if I run the function two times:
list_example = list_move_back(12, list_example]
print(list_example)
>>>[12, 12, 1, 2, 3, 4, 5, 6, 7, 8]
but if I want to run it a third time, the result looks like that:
list_example = list_move_back(12, list_example]
print(list_example)
>>>[12, 12, 1, 1, 3, 4, 5, 6, 7, 8]
The first 1 should be a 12. I have no idea why it doesn't work.
Just use list slicing:
def list_move_back(new_value, list_of_values):
return [new_value] + list_of_values[:-1]
Explanation: list_of_values[:-1] returns all the elements except for the last. By appending it to the new value, you get the wanted result. This answer has a pretty cool explanation of how list slicing works.
Also, if for some reason you'd like the "verbose" way to do this (maybe for an exercise or whatever), here's a way to go about it:
def list_move_back(new_value, list_of_values):
for i in range(len(list_of_values)-1, 0, -1):
list_of_values[i] = list_of_values[i-1]
list_of_values[0] = new_value
return list_of_values
I'd recommend list slicing over this method 9/10 times but again, I'm just leaving this here because there might be a case where someone wants to do this as some sort of mental exercise for indexing.
If you need the list to change in place, you can use the list methods .pop() to remove the last item and .insert(0,value) to add an item to the front:
>>> L = list(range(1,11))
>>> L
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> id(L)
1772071032392
>>> L.pop();L.insert(0,12)
10
>>> L
[12, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> id(L) # same list id, modified in place...
1772071032392

How i can replace None value in list with int?

enter code hereSo, Basically I have a list with int values, and I want to see which values are missing in List. To do that I am comparing Value with a range from 1 to 9 (range(1,9)).
matrix = [None, 8, 3, 9, 2, 1, 6, 5, None]
when i run this code:
for row in matrix:
if range(1,9) not in row :
print(row)
else:
print('wrong logic')
I am getting this error
TypeError: 'NoneType' object cannot be interpreted as an integer
We can see this as a difference of two sets, one with all numbers of interest and one with the numbers in your list. All you need to do is to subtract the set with all numbers by the set with the values in your list.
list = [None, 8, 3, 9, 2, 1, 6, 5, None]
missing_numbers = set(range(1,9)) - set(list)
print(missing_numbers)
This will print {4, 7}.
Remove the Nones from the list before evaluating whether the range is in the list.
start_list = [None, 8, 3, 9, 2, 1, 6, 5, None]
new_list = [item for item in start_list if item is not None]
And then check for numbers 1-9 in the new list.
Another way to do this without even removing the Nones is to just loop through the numbers 1-9 and check if they are in the list.
start_list = [None, 8, 3, 9, 2, 1, 6, 5, None]
missing = []
for num in range(1,10):
if num not in start_list:
missing.append(num)
print(missing)
the range function takes integers as its arguments. you cannot pass it a None value. You can convert your None values to zero's first.
my_list = [None, 8, 3, 9, 2, 1, 6, 5, None]
my_list = [item if item else 0 for item in my_list]
print(my_list)
OUTPUT
[0, 8, 3, 9, 2, 1, 6, 5, 0]
Requirement is not very clear but I suppose you want to compare values to list and finally print the missing numbers.
A possible approach to obtain the missing values in list_to_check is to compare if its entries are present in good_values and, if so, remove them from good_values.
What will be left is just the missing entries in list_to_check.
list_to_check = [None, 8, 3, 9, 2, 1, 6, 5, None]
good_values = list(range(1, len(list_to_check) + 1))
for number in list_to_check:
if number in good_values:
good_values.remove(number)
print(good_values)
Output
[4, 7]
Edit
Now I read from comments that you would also like to add the missing numbers in place of the None items.
Here's a more verbose and easy to understand solution:
list_to_check = [None, 8, 3, 9, 2, 1, 6, 5, None]
good_values = list(range(1, len(list_to_check) + 1))
for number in list_to_check:
if number in good_values:
good_values.remove(number)
result = []
for value in list_to_check:
if value is None:
result.append(good_values.pop())
else:
result.append(value)
print(result)
Output:
[7, 8, 3, 9, 2, 1, 6, 5, 4]
For reasons that I do not fully understand if you are not using list comprehensions in your Python code you are simply not cool.
Let's also be cool, then!
Here's a shorter solution for the same problem:
list_to_check = [None, 8, 3, 9, 2, 1, 6, 5, None]
good_values = list(range(1, len(list_to_check) + 1))
for number in list_to_check:
if number in good_values:
good_values.remove(number)
print([good_values.pop() if value is None else value for value in list_to_check])
Output
[7, 8, 3, 9, 2, 1, 6, 5, 4]

Better way to swap elements in a list?

I have a bunch of lists that look like this one:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I want to swap elements as follows:
final_l = [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
The size of the lists may vary, but they will always contain an even number of elements.
I'm fairly new to Python and am currently doing it like this:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
final_l = []
for i in range(0, len(l)/2):
final_l.append(l[2*i+1])
final_l.append(l[2*i])
I know this isn't really Pythonic and would like to use something more efficient. Maybe a list comprehension?
No need for complicated logic, simply rearrange the list with slicing and step:
In [1]: l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [2]: l[::2], l[1::2] = l[1::2], l[::2]
In [3]: l
Out[3]: [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
 TLDR;
Edited with explanation
I believe most viewers are already familiar with list slicing and multiple assignment. In case you don't I will try my best to explain what's going on (hope I do not make it worse).
To understand list slicing, here already has an excellent answer and explanation of list slice notation.
Simply put:
a[start:end] # items start through end-1
a[start:] # items start through the rest of the array
a[:end] # items from the beginning through end-1
a[:] # a copy of the whole array
There is also the step value, which can be used with any of the above:
a[start:end:step] # start through not past end, by step
Let's look at OP's requirements:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # list l
^ ^ ^ ^ ^ ^ ^ ^ ^ ^
0 1 2 3 4 5 6 7 8 9 # respective index of the elements
l[0] l[2] l[4] l[6] l[8] # first tier : start=0, step=2
l[1] l[3] l[5] l[7] l[9] # second tier: start=1, step=2
-----------------------------------------------------------------------
l[1] l[3] l[5] l[7] l[9]
l[0] l[2] l[4] l[6] l[8] # desired output
First tier will be: l[::2] = [1, 3, 5, 7, 9]
Second tier will be: l[1::2] = [2, 4, 6, 8, 10]
As we want to re-assign first = second & second = first, we can use multiple assignment, and update the original list in place:
first , second = second , first
that is:
l[::2], l[1::2] = l[1::2], l[::2]
As a side note, to get a new list but not altering original l, we can assign a new list from l, and perform above, that is:
n = l[:] # assign n as a copy of l (without [:], n still points to l)
n[::2], n[1::2] = n[1::2], n[::2]
Hopefully I do not confuse any of you with this added explanation. If it does, please help update mine and make it better :-)
Here a single list comprehension that does the trick:
In [1]: l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [2]: [l[i^1] for i in range(len(l))]
Out[2]: [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
The key to understanding it is the following demonstration of how it permutes the list indices:
In [3]: [i^1 for i in range(10)]
Out[3]: [1, 0, 3, 2, 5, 4, 7, 6, 9, 8]
The ^ is the exclusive or operator. All that i^1 does is flip the least-significant bit of i, effectively swapping 0 with 1, 2 with 3 and so on.
You can use the pairwise iteration and chaining to flatten the list:
>>> from itertools import chain
>>>
>>> list(chain(*zip(l[1::2], l[0::2])))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
Or, you can use the itertools.chain.from_iterable() to avoid the extra unpacking:
>>> list(chain.from_iterable(zip(l[1::2], l[0::2])))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
A benchmark between top answers:
Python 2.7:
('inp1 ->', 15.302665948867798) # NPE's answer
('inp2a ->', 10.626379013061523) # alecxe's answer with chain
('inp2b ->', 9.739919185638428) # alecxe's answer with chain.from_iterable
('inp3 ->', 2.6654279232025146) # Anzel's answer
Python 3.4:
inp1 -> 7.913498195000102
inp2a -> 9.680125927000518
inp2b -> 4.728151862000232
inp3 -> 3.1804273489997286
If you are curious about the different performances between python 2 and 3, here are the reasons:
As you can see #NPE's answer (inp1) performs very better in python3.4, the reason is that in python3.X range() is a smart object and doesn't preserve all the items between that range in memory like a list.
In many ways the object returned by range() behaves as if it is a list, but in fact it isn’t. It is an object which returns the successive items of the desired sequence when you iterate over it, but it doesn’t really make the list, thus saving space.
And that's why in python 3 it doesn't return a list while you slice the range object.
# python2.7
>>> range(10)[2:5]
[2, 3, 4]
# python 3.X
>>> range(10)[2:5]
range(2, 5)
The second significant change is performance accretion of the third approach (inp3). As you can see the difference between it and the last solution has decreased to ~2sec (from ~7sec). The reason is because of the zip() function which in python3.X it returns an iterator which produces the items on demand. And since the chain.from_iterable() needs to iterate over the items once again it's completely redundant to do it before that too (what that zip does in python 2).
Code:
from timeit import timeit
inp1 = """
[l[i^1] for i in range(len(l))]
"""
inp2a = """
list(chain(*zip(l[1::2], l[0::2])))
"""
inp2b = """
list(chain.from_iterable(zip(l[1::2], l[0::2])))
"""
inp3 = """
l[::2], l[1::2] = l[1::2], l[::2]
"""
lst = list(range(100000))
print('inp1 ->', timeit(stmt=inp1,
number=1000,
setup="l={}".format(lst)))
print('inp2a ->', timeit(stmt=inp2a,
number=1000,
setup="l={}; from itertools import chain".format(lst)))
print('inp2b ->', timeit(stmt=inp2b,
number=1000,
setup="l={}; from itertools import chain".format(lst)))
print('inp3 ->', timeit(stmt=inp3,
number=1000,
setup="l={}".format(lst)))
One of the possible answer using chain and list comprehension
>>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(chain([(l[2*i+1], l[2*i]) for i in range(0, len(l)/2)]))
[(2, 1), (4, 3), (6, 5), (8, 7), (10, 9)]
Another way, create nested lists with pairs reversing their order, then flatten the lists with itertools.chain.from_iterable
>>> from itertools import chain
>>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(chain.from_iterable([[l[i+1],l[i]] for i in range(0,(len(l)-1),2)]))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
EDIT: I just applied Kasramvd's benchmark test to my solution and I found this solution is slower than the other top answers, so I wouldn't recommend it for large lists. I still find this quite readable though if performance is not critical.
Another approach with simply re-assigning and slicing technique
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for a in range(0,len(l),2):
l[a:a+2] = l[a-len(l)+1:a-1-len(l):-1]
print l
output
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
For fun, if we interpret "swap" to mean "reverse" in a more general scope, the itertools.chain.from_iterable approach can be used for subsequences of longer lengths.
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def chunk(list_, n):
return (list_[i:i+n] for i in range(0, len(list_), n))
list(chain.from_iterable(reversed(c) for c in chunk(l, 4)))
# [4, 3, 2, 1, 8, 7, 6, 5, 10, 9]
An(other) alternative:
final_l = list() # make an empty list
for i in range(len(l)): # for as many items there are in the original list
if i % 2 == 0: # if the item is even
final_l.append(l[i+1]) # make this item in the new list equal to the next in the original list
else: # else, so when the item is uneven
final_l.append(l[i-1]) # make this item in the new list equal to the previous in the original list
This assumes that the original list has an even number of items. If not, a try-except can be added:
final_l = list()
for i in range(len(l)):
if i % 2 == 0:
try: # try if we can add the next item
final_l.append(l[i+1])
except: # if we can't (because i+1 doesnt exist), add the current item
final_l.append(l[i])
else:
final_l.append(l[i-1])
A way using Numpy
import numpy as np
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
l = np.array(l)
final_l = list(np.flip(l.reshape(len(l)//2,2), 1).flatten())
New to stack overflow. Please free to leave a comment or feedback on this solution.
swap = [2, 1, 4, 3, 5]
lst = []
for index in range(len(swap)):
if index%2 == 0 and index < len(swap)-1:
swap[index],swap[index+1] = swap[index+1],swap[index]
lst.append(swap[index])
print(lst)
out = [1, 2, 3, 4, 5]
I don't see anything wrong with your implementation at all. But you could perhaps do a simple swap instead.
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, len(l), 2):
old = l[i]
l[i] = l[i+1]
l[i+1] = old
EDIT
Apparently, Python has a nicer way to do a swap which would make the code like this
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, len(l), 2):
l[i], l[i+1] = l[i+1], l[i]
newList = [(x[2*i+1], x[2*i]) for i in range(0, len(x)/2)]
Now find a way to unzip the tuples. I won't do all of your homework.
Here a solution based in the modulo operator:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even = []
uneven = []
for i,item in enumerate(l):
if i % 2 == 0:
even.append(item)
else:
uneven.append(item)
list(itertools.chain.from_iterable(zip(uneven, even)))

Categories