A list contains another list with repetition - python

I need to check if a list contains every element of another list in python. Not the set operation exactly, because in set distinct values are considered. How can I do it?
Sample:
a is the larger list, b is the smaller set
a = [1, 1, 2, 4], b = [1, 2, 3] -> False
a = [1, 1, 2, 3], b = [1, 2, 3] -> True
a = [1, 2, 4], b = [1, 2, 1] -> False // Because b has two 1s but a has only one.
I would like to request you to look at the third case carefully.
[N. B.] I know exactly how it can be done through hash map. But I want something less bulky.

A simple one-liner with Counter
def isin(a, b): return not (Counter(b) - Counter(a))
Demo:
>>> isin([1, 1, 2, 4], [1, 2, 3])
False
>>> isin([1, 1, 2, 3], [1, 2, 3])
True
>>> isin([1, 1, 2, 4], [1, 2, 1])
True
>>> isin([1, 2, 4], [1, 2, 1])
False

from collections import Counter
def is_contained(a,b):
aCount = Counter(a)
bCount = Counter(b)
return all(aCount[x] >= bCount[x] for x in bCount)
>>> is_contained([1, 1, 2, 4],[1, 2, 3])
False
>>> is_contained([1, 1, 2, 3], [1, 2, 3])
True
>>> is_contained([1, 2, 4], [1, 2, 1])
False

from collections import Counter
def is_contained(a, b):
aCount = Counter(a)
bCount = Counter(b)
# think of it as aCount >= bCount in set-operations
return aCount & bCount == bCount

I asked a similar question, which was closed with a reference to this one. The problem I have with these answers is that they all create a Counter object from the lists. Unless I am misunderstanding something, that is essentially creating a duplicate of both the full and the sub list. This is unimportant if both are small but if you're dealing with large lists, that's inefficient. I went with this:
def is_valid_sub(full_list, sub_list):
matched = []
for sub_item in sub_sublist:
for indx, full_item in enumerate(full_list):
if sub_item == full_item and indx not in matched:
matched.append(indx)
break
return len(matched) == len(sub_list)
Edit: Added more meaningful variable names

def contains(a,b):
for item in set(a):
if item in set(b):
print 'something or do something'

Related

How to iterate over two lists with repeating elements

The list_1 have repeated values that are repeated in list_2 I need to find the elements that match from list_1 and list_2 and save them in result_list_1 and result_list_2 respectively and also the ones that do not match and save them in not_equals_list_1 and not_equals_list_2. The result of the items that match must also match in position.
Note: I need to use only arrays and not sets.
list_1 = [1, 5, 2, 3, 2, 4, 1, 3]
list_2 = [1, 2, 2, 3, 1, 6]
It should result in:
result_list_1 = [1, 2, 3, 2, 1]
result_list_2 = [1, 2, 3, 2, 1]
not_equals_list_1 = [3, 4, 5]
not_equals_list_2 = [6]
Your help would be of great help to me.
My method would decrease your time complexity at the cost of space complexity.
def taketwoarray(arr1,arr2):
my_dict={}
for i in arr1:# add all arr1 elements to dict
my_dict[i]=0
for i in arr2:# match if they are in arr1 increase count else set to -1
try:
if my_dict[i]!=-1:
my_dict[i]+=1
except:
my_dict[i]=-1
#now you have count of numbers that came in both lists and the numbers themselves
#and the numbers that dont match in either would be marked by zero and -1 count.
# count is set to -1 to avoid numbers coming again and again only in arr2.
Let me know if there is any issue!
collections.Counter is invaluable for things like this - like a set but with multiplicity. The algorithm is simple - we make a Counter for each list, and then for the opposite list we just check whether each element is in the other list's counter. If so, remove one occurrence and put it in the result_list. Otherwise, put it in the not_equals_list.
from collections import Counter
list_1 = [1, 5, 2, 3, 2, 4, 1, 3]
list_2 = [1, 2, 2, 3, 1, 6]
counter_1 = Counter(list_1)
counter_2 = Counter(list_2)
result_list_1 = []
result_list_2 = []
not_equals_list_1 = []
not_equals_list_2 = []
for item in list_1:
if counter_2[item] > 0:
result_list_1.append(item)
counter_2[item] -= 1
else:
not_equals_list_1.append(item)
for item in list_2:
if counter_1[item] > 0:
result_list_2.append(item)
counter_1[item] -= 1
else:
not_equals_list_2.append(item)
print(result_list_1) # [1, 2, 3, 2, 1]
print(result_list_2) # [1, 2, 2, 3, 1]
print(not_equals_list_1) # [5, 4, 3]
print(not_equals_list_2) # [6]
Order is preserved in not_equals_list from the order of the first list. If you desire it differently, you can use reversed() where necessary to change either order of iteration or to simply flip the result.
If using this type of solution with custom objects, you'll need to make sure that __hash__() is properly implemented for equality checking - since both sets and dicts are hashtable-based, and Counter is just a subclass of dict.
From a quick google search, multiset might provide a more efficient way of doing this by just converting your lists into sets and then doing set operations on them. I haven't tested this, though.
result_list_1 = []
result_list_2 = []
not_equals_list_1 = []
not_equals_list_2 = []
for item in list_1:
if item in list_2:
result_list_1.append(item)
else:
not_equals_list_1.append(item)
for item in list_2:
if item in list_1:
result_list_2.append(item)
else:
not_equals_list_2.append(item)
from collections import Counter
list_1 = [1, 5, 2, 3, 2, 4, 1, 3]
list_2 = [1, 2, 2, 3, 1, 6]
c1 = Counter(list_1)
c2 = Counter(list_2)
result_list_1 = [*(c1 & c2).elements()]
result_list_2 = result_list_1[:]
not_equals_list_1 = [*(c1 - c2).elements()]
not_equals_list_2 = [*(c2 - c1).elements()]
print(result_list_1) # [1, 1, 2, 2, 3]
print(result_list_2) # [1, 1, 2, 2, 3]
print(not_equals_list_1) # [5, 3, 4]
print(not_equals_list_2) # [6]

Repeat values in a list (having two list) in python

I'm coding in Python, I have an exercise like this :
long = [5, 2, 4]
number = [1, 2, 3]
ids = []
I want to have :
ids = [1, 1, 1, 1, 1, 2, 2, 3, 3, 3, 3]
I want to repeat 5 times 1, 2 times 2, 4 times 3.
I don't know how to do it.
from collections import Counter
long = [5, 2, 4]
number = [1, 2, 3]
ids = list(Counter(dict(zip(number, long))).elements())
print(ids)
You can do it with a simple loop which will iterate over (times-to-repeat, number) pairs and extend your output list with generated list of numbers :
for times, n in zip(long, number):
ids.extend([n] * times)
print(ids) # [1, 1, 1, 1, 1, 2, 2, 3, 3, 3, 3]
They've provided you with two very good solutions, but I'll leave the brute force approach (hardly ever the best one) in here, since it's the one you're likely more prone to understand:
long = [5, 2, 4]
number = [1, 2, 3]
ids = []
for i in range(len(long)):
aux = 0
while (aux < long[i]):
ids.append(number[i])
aux += 1
print(ids)

How to reorder list in Python to avoid repeating elements?

I am trying to check if a list has any consecutive repeating elements and then reorder it such that the repeats are avoided. If that is impossible, then return False. For example:
checkRepeat([1,2])
Out[61]: [1, 2]
checkRepeat([1,2,2])
Out[62]: [2, 1, 2]
checkRepeat([1,2,2,1,1])
Out[63]: [1, 2, 1, 2, 1]
checkRepeat([1,2,2,1,1,3,3,3,3])
Out[64]: [1, 3, 1, 3, 2, 1, 3, 2, 3]
checkRepeat([1,2,2,1,1,3,3,3,3,3])
Out[65]: [3, 1, 3, 2, 3, 1, 3, 1, 3, 2]
checkRepeat([1,2,2,1,1,3,3,3,3,3,3])
Out[66]: [3, 1, 3, 1, 3, 1, 3, 2, 3, 2, 3]
checkRepeat([1,2,2,1,1,3,3,3,3,3,3,3])
Out[67]: False
Here is what I have. Is there a more elegant solution?
from itertools import groupby
def checkRepeat(lst,maxIter=1000):
"""Returns a list that has no repeating elements. Will try for a max of 1000 iterations by default and return False if such a list can't be found"""
def hasRepeat(lst):
"""Returns true if there are any repeats"""
return len([x[0] for x in groupby(lst)]) < len(lst)
offset=numIter=0
while hasRepeat(lst) and numIter<maxIter:
for i,curElt in enumerate(lst):
try:
if lst[i]==lst[i+1]:
lst[i+1],lst[(i+offset) % len(lst)] = lst[(i+offset) % len(lst)],lst[i+1] #swap j+1 with j+offset. wrap around the list
except:
break
offset+=1
numIter+=1
if numIter==maxIter:
return False
else:
return lst
Here's the algorithm I alluded to in comments implemented, using the very useful collections.Counter class:
from collections import Counter
def check_repeat(sequence):
if not sequence:
return []
element_counts = Counter(sequence)
new_sequence = []
elements_chosen = 0
elements_needed = len(sequence)
previous_element_chosen = None
while elements_chosen < elements_needed:
candidates_needed = 1 if previous_element_chosen is None else 2
candidates = element_counts.most_common(candidates_needed)
candidate = (candidates[0] if
(len(candidates) < 2 or candidates[0][0] != previous_element_chosen)
else candidates[1])
if candidate[1] <= 0:
return False
else:
new_sequence.append(candidate[0])
element_counts[candidate[0]] -= 1
previous_element_chosen = candidate[0]
elements_chosen += 1
return new_sequence
This needs some refinement if None is a valid value in your sequence, or if you care about stability to any degree. If the elements of your sequence are not hashable, it won't work at all.
And that ternary candidate = ... assignment is probably clearer broken up a bit more.
You may try probability equation
Number which is repeating highest number of times must be always lessthen count of other numbers.
[1,2,2,1,1,3,3,3,3,3,3,3]
7< (3+2) false
[1,2,2,1,1,3,3,3,3,3,3]
6< (3+2) true
[1,2,2,1,1,3,3,3,3,3]
5< (3+3) true
code
from itertools import groupby
>>> a =[1,2,2,1,1,3,3,3,3,3]
>>> s = [len(list(group)) for key, group in groupby(a)]
>>> s
[1, 2, 2, 5]
>>> max(s) < (sum(s)-max(s))
True

How to change numbers around in a list (python)

i have been working on this for 3 hours now but i have no clue how to do it
can anyone help me with this?
values = [1, 2, 3, 4, 5]
temp = values[0]
for index in range (len(values) -1):
values[index] = values [index]
values[len(values)-1] = temp
print values
i want the printed values to be in order as [2,3,4,5,1]
by simply changing those in the brackets
deque is more efficient way to do
In [1]: import collections
In [3]: dq = collections.deque([1,2,3,4,5])
In [4]: dq.rotate(-1)
In [5]: dq
Out[5]: deque([2, 3, 4, 5, 1])
What you are trying to achieve is not available in the python libraries but you can leverage slicing to rotate list
Implementation
def rotate(seq, n = 1, direc = 'l'):
if direc.lower() == 'l':
seq = seq[n:] + seq[0:n]
else:
seq = seq[-n:] + seq[:-n]
return seq
Demonstration
>>> rotate(values)
[2, 3, 4, 5, 1]
>>> rotate(values,2,'l')
[3, 4, 5, 1, 2]
>>> rotate(values,2,'r')
[4, 5, 1, 2, 3]
Simple but powerful slice syntax:
values = [1, 2, 3, 4, 5]
shifted = values[1:]+values[:1]
assert shifted == [2, 3, 4, 5, 1]
How about time one-liner, in the spirit of sleepsort:
while values != [2, 3, 4, 5, 1]: random.shuffle(values)

Python check next three elements in list [duplicate]

This question already has answers here:
Check for presence of a sliced list in Python
(11 answers)
Closed 9 years ago.
I am not sure if this question has been asked before but I couldn't find anything similar from the question list. I would like to check if a list has a set of three values in a certain order. For example, I would like to check if an int list has a set of values 1, 2, 3 anywhere within that list. The length of the list is unknown and the list cannot be sorted.
Example:
Values to check: 1, 2, 3 (in this order)
Example of a list = [1, 1, 2, 3, 1]
This is what I have tried so far.
list1 = [1, 1, 2, 3, 1]
list2 = [1, 1, 4, 3, 1, 2, 1]
def checkValue:
for i in range (0, len(nums)):
if (nums[i+2] - nums[i+1] == nums[i+1] - nums[i]) == 1:
return True
return False
list1 --> return True
list2 ---> IndexError: list index out of range
EDIT: Thanks to those who answered and thank you for the list to the sublist question. I never thought that the set of integers can be considered as a sublist and use it to compare to a larger list.
It looks like you're searching a sequence in a list.
You can just compare parts of the list with the sequence.
def find_sequence_in_list(list_to_check, values):
for i in range (len(list_to_check) - len(values) + 1):
#print(list_to_check[i:i + len(values)])
if list_to_check[i:i + len(values)] == values:
return True
return False
values = [1, 2, 3]
data1 = [1, 1, 2, 3, 1]
data2 = [1, 1, 4, 3, 1, 2, 1]
print(find_sequence_in_list(data1, values))
print(find_sequence_in_list(data2, values))
Uncomment the print to see what's happening.
i + 2 is too large in the loop body, nums doesn't have that many elements. Fix it like this:
if i + 2 < len(nums) and (nums[i+2] - nums[i+1] == nums[i+1] - nums[i]) == 1:
...
You can use tuple comparison directly, along with zip iteration (or itertools.izip if you prefer, for general iterables):
>>> def findin(values, nums):
... t = tuple(values)
... return any(T == t for T in zip(*(nums[i:] for i in xrange(values))))
Which gives for your examples:
>>> findin([1,2,3], [1,1,2,3,1])
True
>>> findin([1,2,3], [1, 1, 4, 3, 1, 2, 1])
False
I'm thinking of using deque to the sublist comparison.
from collections import deque
def has_sublist(lst, sub):
tmp_q = deque([], maxlen=len(sub))
sub_q = deque(sub)
for i in nums:
if tmp_q == sub_q:
return True
else:
tmp_q.append(i)
return tmp_q == sub_q
The tmp_q has a max length of len(sub) (which is 3 in your example), it contains a sublist of list to search in.
Let's check if it works well:
>>> lst = [1, 1, 4, 3, 1, 2, 1]
>>> sub = [1, 2, 3]
>>> print has_sublist(lst, sub)
False
>>> lst = [1, 1, 4, 3, 1, 2, 3]
>>> print has_sublist(lst, sub)
True
>>> lst = [1, 2]
>>> print find(lst, sub)
False
>>> lst = [1, 2, 3]
>>> print has_sublist(lst, sub)
True
In this case, you have no need to worry about the IndexError.

Categories

Resources