return unique python lists of chars ignoring order - python-3.x

Problem:
Consider a python list of lists that contains a sequence of chars:
[['A', 'B'],['A','B','C'],['B','A'],['C','A','B'],['D'],['D'],['Ao','B']]
The goal is to return the unique lists, regardless of order:
[['A','B'],['A','B','C'],['D'],['Ao','B']]
Attempt:
I'm able to achieve my goal using many if/else statements with try/exceptions. What would be the most pythonic (faster) way to approach this problem? Thanks!
def check_duplicates(x,list_):
for li in list_:
if compare(x,li):
return True
def compare(s, t):
t = list(t) # make a mutable copy
try:
for elem in s:
t.remove(elem)
except ValueError:
return False
return not t
vars_list = [['A', 'B'],['A','B','C'],['B','A'],['C','A','B'],['D'],['D'],['Ao','B']]
second_list = []
for i in vars_list:
if check_duplicates(i,second_list):
continue
else:
second_list.append(i)
print(i)

Assuming that the elements of the nested lists are hashable, you can isolate the unique collections by constructing a set of frozensets from the nested list:
unique_sets = {frozenset(l) for l in vars_list}
# {frozenset({'D'}),
# frozenset({'A', 'B'}),
# frozenset({'A', 'B', 'C'}),
# frozenset({'Ao', 'B'})}
If you need a list-of-lists as the output, you can obtain one trivially with [list(s) for s in unique_sets].

Related

Find an index in a list of lists using an index inside one of the lists in pyton

I'm trying to determine if there is a way to access an index essentially by making a list of lists, where each inner list has a tuple that provides essentially grid coordinates, i.e:
example = [
['a', (0,0)], ['b',(0,1)], ['c', (0,2)],
['d', (1,0)], ['e',(1,1)], ['d', (1,2)],
.....
]
and so on.
So, If I have coordinates (0,1), I want to be able to return example[1][0], or at the very least example[1] since these coordinates correlate with example[1].
I tried using index(), but this doesn't go deep enough. I also looked into itertools, but I cannot find a tool that finds it and doesn't return a boolean.
Using a number pad as an example:
from itertools import chain
def pinpad_test():
pad=[
['1',(0,0)],['2',(0,1)],['3',(0,2)],
['4',(1,0)],['5',(1,1)],['6',(1,2)],
['7',(2,0)],['8',(2,1)],['9',(2,2)],
['0',(3,1)]
]
tester = '1234'
print(tester)
for dig in tester:
print(dig)
if dig in chain(*pad):
print(f'Digit {dig} in pad')
else:
print('Failed')
print('end of tester')
new_test = pad.index((0,1)in chain(*pad))
print(new_test)
if __name__ == '__main__':
pinpad_test()
I get an value error at the initiation of new_test.
You can just yield from simple generator expression:
coords = (0, 1)
idx = next((sub_l[0] for sub_l in pad if sub_l[1] == coords), None)
print(idx)
2
You can create a function that will give you want
def on_coordinates(coordinates:tuple, list_coordinates:list):
return next(x for x in list_coordinatesif x[1] == coordinates)

Python : Create a function that takes a list of integers and strings and returns a new list with the strings filtered out

I am new to coding in Python and I am struggling with a very simple problem. There is the same question but for javascript on the forum but it does not help me.
My code is :
def filter_list(l):
for i in l:
if i != str():
l.append(i)
i = i + 1
return(l)
print(filter_list([1,2,'a','b']))
If you can help!
thanks
Before I present solution here are some problems you need to understand.
str()
str() creates a new instance of the string class. Comparing it to an object with == will only be true if that object is the same string.
print(1 == str())
>>> False
print("some str" == str())
>>> False
print('' == str())
>>> True
iterators (no +1)
You have i = i + 1 in your loop. This doesn't make any sense. i comes from for i in l meaning i looping over the members of list l. There's no guarantee you can add 1 to it. On the next loop i will have a new value
l = [1,2,'a']
for i in l:
print(i)
>>> 1
>>> 2
>>> 'a'
To filter you need a new list
You are appending to l when you find a string. This means that when your loop finds an integer it will append it to the end of the list. And later it will find that integer on another loop interation. And append it to the end AGAIN. And find it in the next iteration.... Forever.
Try it out! See the infinite loop for yourself.
def filter_list(l):
for i in l:
print(i)
if type(i) != str:
l.append(i)
return(l)
filter_list([1,2,'a','b'])
Fix 1: Fix the type check
def filter_list(l):
for i in l:
if type(i) != str:
l.append(i)
return(l)
print(filter_list([1,2,'a','b']))
This infinite loops as discussed above
Fix 2: Create a new output array to push to
def filter_list(l):
output = []
for i in l:
if type(i) != str:
output.append(i)
return output
print(filter_list([1,2,'a','b']))
>>> [1,2]
There we go.
Fix 3: Do it in idiomatic python
Let's use a list comprehension
l = [1,2,'a','b']
output = [x for x in l if type(x) != str]
print(output)
>>> [1, 2]
A list comprehension returns the left most expression x for every element in list l provided the expression on the right (type(x) != str) is true.

How search an unordered list for a key using reduce?

I have a basic reduce function and I want to reduce a list in order to check if an item is in the list. I have defined the function below where f is a comparison function, id_ is the item I am searching for, and a is the list. For example, reduce(f, 2, [1, 6, 2, 7]) would return True since 2 is in the list.
def reduce(f, id_, a):
if len(a) == 0:
return id_
elif len(a) == 1:
return a[0]
else:
# can call these in parallel
res = f(reduce(f, id_, a[:len(a)//2]),
reduce(f, id_, a[len(a)//2:]))
return res
I tried passing it a comparison function:
def isequal(x, element):
if x == True: # if element has already been found in list -> True
return True
if x == element: # if key is equal to element -> True
return True
else: # o.w. -> False
return False
I realize this does not work because x is not the key I am searching for. I get how reduce works with summing and products, but I am failing to see how this function would even know what the key is to check if the next element matches.
I apologize, I am a bit new to this. Thanks in advance for any insight, I greatly appreciate it!
Based on your example, the problem you seem to be trying to solve is determining whether a value is or is not in a list. In that case reduce is probably not the best way to go about that. To check if a particular value is in a list or not, Python has a much simpler way of doing that:
my_list = [1, 6, 2, 7]
print(2 in my_list)
print(55 in my_list)
True
False
Edit: Given OP's comment that they were required to use reduce to solve the problem, the code below will work, but I'm not proud of it. ;^) To see how reduce is intended to be used, here is a good source of information.
Example:
from functools import reduce
def test_match(match_params, candidate):
pattern, found_match = match_params
if not found_match and pattern == candidate:
match_params = (pattern, True)
return match_params
num_list = [1,2,3,4,5]
_, found_match = reduce(test_match, num_list, (2, False))
print(found_match)
_, found_match = reduce(test_match, num_list, (55, False))
print(found_match)
Output:
True
False

i want to find a common character from n number string inside a single multidimensional list using python

def bibek():
test_list=[[]]
x=int(input("Enter the length of String elements using enter -: "))
for i in range(0,x):
a=str(input())
a=list(a)
test_list.append(a)
del(test_list[0]):
def filt(b):
d=['b','i','b']
if b in d:
return True
else:
return False
for t in test_list:
x=filter(filt,t)
for i in x:
print(i)
bibek()
suppose test_list=[['b','i','b'],['s','i','b'],['r','i','b']]
output should be ib since ib is common among all
an option is to use set and its methods:
test_list=[['b','i','b'],['s','i','b'],['r','i','b']]
common = set(test_list[0])
for item in test_list[1:]:
common.intersection_update(item)
print(common) # {'i', 'b'}
UPDATE: now that you have clarified your question i would to this:
from difflib import SequenceMatcher
test_list=[['b','i','b','b'],['s','i','b','b'],['r','i','b','b']]
# convert the list to simple strings
strgs = [''.join(item) for item in test_list]
common = strgs[0]
for item in strgs[1:]:
sm = SequenceMatcher(isjunk=None, a=item, b=common)
match = sm.find_longest_match(0, len(item), 0, len(common))
common = common[match.b:match.b+match.size]
print(common) # 'ibb'
the trick here is to use difflib.SequenceMatcher in order to get the longest string.
one more update after clarification of your question this time using collections.Counter:
from collections import Counter
strgs='app','bapp','sardipp', 'ppa'
common = Counter(strgs[0])
print(common)
for item in strgs[1:]:
c = Counter(item)
for key, number in common.items():
common[key] = min(number, c.get(key, 0))
print(common) # Counter({'p': 2, 'a': 1})
print(sorted(common.elements())) # ['a', 'p', 'p']

Inquiry about removing duplicates

Alright so I'm required to eliminate spaces and duplicate values in a list (of only numbers). Here's my code:
def eliminateDuplicates(lst):
i=0
while i<len(lst):
while lst.count(lst[i])!=1:
lst.remove(lst[i])
i=i+1
print(lst)
def main():
a=input("Enter numbers: ")
lst=list(a)
while ' ' in lst:
lst.remove(' ')
eliminateDuplicates(lst)
main()
while this method is effective and works, when the input is say
Enter numbers: 1 2 3 4 5 3 2 1 1 22
The output results in
['4', '5', '3', '1', '2']
I need my program to recognize 22 and 2 as different items so it doesn't delete the last 2 and the 2 in 22. Any suggestions?
EDIT: Sorry to the two posters that have already given me answers. I am not allowed to use the set function, and order does not matter.
This doesn't do what you think it does:
b="".join(a) # doesn't do anything useful since `a` is already a string
lst=list(b) # this is converting the string to a list of characters
Try this instead:
lst = a.split() # automatically cleans up the whitespace for you
print(list(set(lst)))
Turning a list into a set and back again is a handy way to remove duplicates. It's also quite efficient compared to the way you are doing it by scanning the list over and over
If you really want to keep the eliminateDuplicates function then it can just be
def eliminate_duplicates(lst):
return list(set(lst))
def main():
a=input("Enter numbers: ")
lst = a.split() # split automatically cleans up the whitespace
print(eliminate_duplicates(lst))
if __name__ == "__main__":
main()
Edit: since you're not allowed to use set, Collections is another fairly efficient method to remove duplicates
from collections import Counter
def eliminate_duplicates(lst):
return list(Counter(lst))
This is not quite so efficient, but still much better than two nested loops
from itertools import groupby
def eliminate_duplicates(lst):
[k for k,g in groupby(sorted(lst))]
Does order matter? If not cast it to a set and then cast it back to a list.
lst = [1,2,3,3,6,4,5,6, 3, 22]
lst2 = list(set(lst))
Also, you should probably use lst = a.split(' ') rather than join
def main():
a=input("Enter numbers: ") # Input the numbers
clean_a = a.strip(); #Cleans trailing white space.
lst=list(set(clean_a.split(' '))) #Split into tokens, and remove duplicates

Resources