How to delete repeat elements in this list? - python

I have a list is a = ['R','R','R','B','R','B','B','S','S']. my goal is to delete repeat 'R's and 'S's and then delete the 'B's (if there is only one R or S, just keep it). Therefore, I want the output to be ['R','R','S'], but mine is ['R', 'S'].
Can anyone help me take look my code? Thank you
This is my code
a = ['R','R','R','B','R','B','B','S','S'] # create a list to store R S B
a = [x for x in a if x != 'B'] # Delete all the B's
new_list = [] # create another list to store R and S without repeat
last = None
for x in a:
if last == x and (len(new_list) == 0 or new_list[-1] != x):
new_list.append(last)
last = x
print(new_list)
My output is this
['R', 'S']
but I want this
['R','R','S']

You could use itertools.groupby to group the elements first, then delete the B values:
from itertools import groupby
a = ['R','R','R','B','R','B','S','S'] # create a list to store R S B
[k for k, v in groupby(a) if k != 'B']
Result:
['R', 'R', 'S']

You could try this. This creates a new list without anything that is a repeat, and no 'B's.
a = ['R','R','R','B','R','B','B','S','S']
new_list = [] # create another list to store R and S without repeat
last = None
for x in a:
if last != x and x!='B':
new_list.append(x)
last = x
print(new_list)
Another option is to use a list comprehension:
a = ['R','R','R','B','R','B','B','S','S']
new_list = [ x for i,x in enumerate(a) if (a[i-1] != x and x!='B') or (i==0) ]
print(new_list)
Output from either example is the same:
['R', 'R', 'S']
Neither of these options require an import. However, I think the groupby code given by Mark Meyer is what I'd use in most cases.

You can use fromkeys in this case.
mylist = ["a", "b", "a", "c", "c"]
mylist = list(dict.fromkeys(mylist))
print(mylist) # ['a', 'b', 'c']

Related

Filter a list by omitting values by indexes

I did not find an easy way to filter a list excluding values by their indexes. For example, I have:
varList = ['r', 2, ('c'), ['e'], 'etc.', { 'su': 'lt'}]
indexList = [1, 2, 4]
and I need to have:
expected = ['r', ['e'], {'su': 'lt'}]
The idiomatic way would be:
[x for i, x in enumerate(varList) if i not in indexList]
which is O(m * n) for m = len(varList) and n = len(indexList). While that is okay for short exclusion lists, for some significant speedup with longer lists you may want to say instead:
indexset = set(indexList)
[x for i, x in enumerate(varList) if i not in indexset]
Which is O(m) only (plus O(n) to make the set).
The easiest way to do this would be using list comprehension:
[value for index,value in enumerate(varList) if index not in indexList]
An in-place solution:
for i in indexList[::-1]:
del varList[i]
Assuming the list of indexes is ascending.
It is necessary to loop from the end to keep the indexes true (if we start by deleting index 1, the element in index 2 will move to index 1 and then we will delete the wrong element). For a more general solution use sorted(indexList, reverse=True).
One of the solution is as the following with the options to omit and to extract:
def getFilteredListByIndexes(
varList: list, indexList: list, mode: str = 'omit') -> list:
""" #Function to filter varlist by indexList
#Args:
varList: list or tuple of any type
indexList: list of tuple of integers
mode: str, that could be 'omit' or 'extract'
"""
resTuple = ()
res = []
if mode == 'omit':
resTuple = list(filter(lambda x: not x[0] in indexList, enumerate(varList)))
elif mode == 'extract':
resTuple = list(filter(lambda x: x[0] in indexList, enumerate(varList)))
else:
resTuple = varList.emumerate()
res = list(map(lambda x: x[1], resTuple))
return res
Usage:
varList = ['r', 2, ('c'), ['e'], 'etc.', { 'su': 'lt'}]
indexList = [1, 2, 4]
expected = ['r', ['e'], {'su': 'lt'}]
res = getFilteredListByIndexes(varList, indexList, 'omit')
print(res) # ['r', ['e'], {'su': 'lt'}]

Remove First occurance of an element from list python

I have two lists:
List_main = ['a','b','c','d','e','f','g','h']
List_sub = ['c','f','d']
I now joined these lists into:
list_joined = ['a','b','c','d','e','f','g','h','c','f','d']
I want to remove the first occurrences of elements in List_sub from the joined list 'list_joined'.
Final output i am looking for is to obtain a list which has the List_sub elements appended at the end.:
['a','b','e','g','h','c','f','d']
I tried an approach where i found out the index of sub_list elements from the List_main.
[i for i,x in enumerate(path_stores_new) if x == value ]
and then use
del list_joined[index]
But i think there may be a better way of doing this.
Any leads?
Note: List_main has unique values
Can be done easily using a list comprehension
a one liner for this is as in the code below:
List_main = ['a','b','c','d','e','f','g','h']
List_sub = ['c','f','d']
list_joined = [i for i in List_main if i not in List_sub] + List_sub
EDIT:
The above code ignored all the occurrences if only the first occurrence is to be ignored the code below should do the job.
List_main = ['a','b','c','d','e','f','g','h']
List_sub = ['c','f','d']
list_joined = [value for ind, value in enumerate(List_main) if value not in List_sub or ind > List_main.index(value)] + List_sub
Alternatively, you can also use the built-in filter function like this:
List_main = ['a','b','c','d','e','f','g','h']
List_sub = ['c','f','d']
sub_filter = lambda x: x not in List_sub
list_joined = list(filter(sub_filter, List_main)) + List_sub
This will output ['a', 'b', 'e', 'g', 'h', 'c', 'f', 'd'].
I wouldn't do exactly the same as #Rakesh, I'm just sending what I would have done even though our answers are very very similar. :)
arr = [1, 2, 3, 4, 5]
arr_2 = [2, 3]
for elem in arr_2:
if elem in arr:
arr.remove(elem)
arr += arr_2
print(arr)
# [1, 4, 5, 2, 3]
You can filter elements of first list witch not in the second, and then extend the result with the second list :
List_main = ['a','b','c','d','e','f','g','h']
List_sub = ['c','f','d']
list(filter(lambda x : x not in List_sub, List_main)) + List_sub
Output :
['a', 'b', 'e', 'g', 'h', 'c', 'f', 'd']
try this:
from itertools import filterfalse
def f(x):
if x in List_sub:
List_sub.remove(x)
return True
return False
list(filterfalse(f, list_joined))
You can convert List_main to a dict first, delete the keys based on List_sub, and then join the remaining keys with List_sub, so that the removal of each element in List_sub can be done in an average time complexity of O(1) (otherwise element lookup and removal in list and would cost an average of O(n) per element):
dict_main = dict.fromkeys(List_main)
for k in List_sub:
del dict_main[k]
print(list(dict_main) + List_sub)
This outputs:
['a', 'b', 'e', 'g', 'h', 'c', 'f', 'd']
If you aren't using Python 3.7 you can use collections.OrderedDict instead of dict.
Do something like this, if you want to ignore the first occurrence:
List_main = ['a','b','c','d','e','f','g','h']
List_sub = ['c','f','d']
dict_main = dict.fromkeys(List_sub)
list_joined = []
for x in List_main:
if x in dict_main.keys():
if dict_main[x] is None:
dict_main[x] = 1
else:
list_joined += x
else:
list_joined += x
list_joined += List_sub
print(list_joined)
You can remove it using this code:
list_main = ['a','b','c','d','e','f','g','h']
list_sub = ['c','f','d']
for sub_item in list_sub:
if sub_item in list_main:
list_main.remove(sub_item)
list_main += sub_item
Or just not add a duplicated items:
list_main = ['a','b','c','d','e','f','g','h']
list_sub = ['c','f','d']
for sub_item in list_sub:
if sub_item not in list_main:
list_main.append(sub_item)
Also if you don't want to have duplicated items you can use set. But be careful, set doesn't keep your initial order of elements.
list_main = ['a','b','c','d','e','f','g','h']
list_sub = ['c','f','d']
result_list = list(set(list_main + list_sub))

How do I pick an arbitrary number of an element occurring many times in a list?

I have two variables holding a string each and an empty list:
a = 'YBBB'
b = 'RYBB'
x = []
I want to loop through each of the strings and treat each 'B' in the two lists as an independent element (wish I could just type a.('B') and b.('B'). What I actually want to do is loop through b and ask if each of the items in b are in a. If so, the length of the item in b (say'B') is checked for in a. This should give 3. Then I want to compare the lengths of the item in the two lists and push the lesser of the two into the empty list. In this case, only two 'B's will be pushed into x.
You can use a nested list comprehension like following:
>>> [i for i in set(b) for _ in range(min(b.count(i), a.count(i)))]
['B', 'B', 'Y']
If the order is important you can use collections.OrderedDict for creating the unique items from b:
>>> from collections import OrderedDict
>>>
>>> [i for i in OrderedDict.fromkeys(b) for _ in range(min(b.count(i), a.count(i)))]
['Y', 'B', 'B']
This is useless text for the moderators.
import collections
a = 'YBBB'
b = 'RYBB'
x = []
a_counter = collections.Counter(a)
b_counter = collections.Counter(b)
print(a_counter)
print(b_counter)
for ch in b:
if a_counter[ch]:
x.append(min(a_counter[ch], b_counter[ch]) * ch)
print(x)
--output:--
Counter({'B': 3, 'Y': 1})
Counter({'B': 2, 'Y': 1, 'R': 1})
['Y', 'BB', 'BB']
Or, if you only want to step through each unique element in b:
for ch in set(b):
if a_counter[ch]:
x.append(min(a_counter[ch], b_counter[ch]) * ch)
print(x)
--output:--
['Y', 'BB']

Python: Append double items to new array

lets say I have an array "array_1" with these items:
A b A c
I want to get a new array "array_2" which looks like this:
b A c A
I tried this:
array_1 = ['A','b','A','c' ]
array_2 = []
for item in array_1:
if array_1[array_1.index(item)] == array_1[array_1.index(item)].upper():
array_2.append(array_1[array_1.index(item)+1]+array_1[array_1.index(item)])
The problem: The result looks like this:
b A b A
Does anyone know how to fix this? This would be really great!
Thanks, Nico.
It's because you have 2 'A' in your array. In both case for the 'A',
array_1[array_1.index(item)+1
will equal 'b' because the index method return the first index of 'A'.
To correct this behavior; i suggest to use an integer you increment for each item. In that cas you'll retrieve the n-th item of the array and your program wont return twice the same 'A'.
Responding to your comment, let's take back your code and add the integer:
array_1 = ['A','b','A','c' ]
array_2 = []
i = 0
for item in array_1:
if array_1[i] == array_1[i].upper():
array_2.append(array_1[i+1]+array_1[i])
i = i + 1
In that case, it works but be careful, you need to add an if statement in the case the last item of your array is an 'A' for example => array_1[i+1] won't exist.
I think that simple flat list is the wrong data structure for the job if each lower case letter is paired with the consecutive upper case letter. If would turn it into a list of two-tuples i.e.:
['A', 'b', 'A', 'c'] becomes [('A', 'b'), ('A', 'c')]
Then if you are looping through the items in the list:
for item in list:
print(item[0]) # prints 'A'
print(item[1]) # prints 'b' (for first item)
To do this:
input_list = ['A', 'b', 'A', 'c']
output_list = []
i = 0;
while i < len(input_list):
output_list.append((input_list[i], input_list[i+1]))
i = i + 2;
Then you can swap the order of the upper case letters and the lower case letters really easily using a list comprehension:
swapped = [(item[1], item[0]) for item in list)]
Edit:
As you might have more than one lower case letter for each upper case letter you could use a list for each group, and then have a list of these groups.
def group_items(input_list):
output_list = []
current_group = []
while not empty(input_list):
current_item = input_list.pop(0)
if current_item == current_item.upper():
# Upper case letter, so start a new group
output_list.append(current_group)
current_group = []
current_group.append(current_item)
Then you can reverse each of the internal lists really easily:
[reversed(group) for group in group_items(input_list)]
According to your last comment, you can get what you want using this
array_1 = "SMITH Mike SMITH Judy".split()
surnames = array_1[1::2]
names = array_1[0::2]
print array_1
array_1[0::2] = surnames
array_1[1::2] = names
print array_1
You get:
['SMITH', 'Mike', 'SMITH', 'Judy']
['Mike', 'SMITH', 'Judy', 'SMITH']
If I understood your question correctly, then you can do this:
It will work for any length of array.
array_1 = ['A','b','A','c' ]
array_2 = []
for index,itm in enumerate(array_1):
if index % 2 == 0:
array_2.append(array_1[index+1])
array_2.append(array_1[index])
print array_2
Output:
['b', 'A', 'c', 'A']

Difference Between Two Lists with Duplicates in Python

I have two lists that contain many of the same items, including duplicate items. I want to check which items in the first list are not in the second list. For example, I might have one list like this:
l1 = ['a', 'b', 'c', 'b', 'c']
and one list like this:
l2 = ['a', 'b', 'c', 'b']
Comparing these two lists I would want to return a third list like this:
l3 = ['c']
I am currently using some terrible code that I made a while ago that I'm fairly certain doesn't even work properly shown below.
def list_difference(l1,l2):
for i in range(0, len(l1)):
for j in range(0, len(l2)):
if l1[i] == l1[j]:
l1[i] = 'damn'
l2[j] = 'damn'
l3 = []
for item in l1:
if item!='damn':
l3.append(item)
return l3
How can I better accomplish this task?
You didn't specify if the order matters. If it does not, you can do this in >= Python 2.7:
l1 = ['a', 'b', 'c', 'b', 'c']
l2 = ['a', 'b', 'c', 'b']
from collections import Counter
c1 = Counter(l1)
c2 = Counter(l2)
diff = c1-c2
print list(diff.elements())
Create Counters for both lists, then subtract one from the other.
from collections import Counter
a = [1,2,3,1,2]
b = [1,2,3,1]
c = Counter(a)
c.subtract(Counter(b))
To take into account both duplicates and the order of elements:
from collections import Counter
def list_difference(a, b):
count = Counter(a) # count items in a
count.subtract(b) # subtract items that are in b
diff = []
for x in a:
if count[x] > 0:
count[x] -= 1
diff.append(x)
return diff
Example
print(list_difference("z y z x v x y x u".split(), "x y z w z".split()))
# -> ['y', 'x', 'v', 'x', 'u']
Python 2.5 version:
from collections import defaultdict
def list_difference25(a, b):
# count items in a
count = defaultdict(int) # item -> number of occurrences
for x in a:
count[x] += 1
# subtract items that are in b
for x in b:
count[x] -= 1
diff = []
for x in a:
if count[x] > 0:
count[x] -= 1
diff.append(x)
return diff
Counters are new in Python 2.7.
For a general solution to substract a from b:
def list_difference(b, a):
c = list(b)
for item in a:
try:
c.remove(item)
except ValueError:
pass #or maybe you want to keep a values here
return c
you can try this
list(filter(lambda x:l1.remove(x),li2))
print(l1)
Try this one:
from collections import Counter
from typing import Sequence
def duplicates_difference(a: Sequence, b: Sequence) -> Counter:
"""
>>> duplicates_difference([1,2],[1,2,2,3])
Counter({2: 1, 3: 1})
"""
shorter, longer = sorted([a, b], key=len)
return Counter(longer) - Counter(shorter)

Categories

Resources