How to iterate over two lists with repeating elements - python

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]

Related

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)

Python list comprehension mirroring values

I tried to figure out the following problem through list comprehension but I couldn't make it work. I will show you how I solved the problem using a loop and a list comprehension.
So, I have a list that can have 0 to 6 elements in a range(6) and when I apply my function on it I want to change the values, as shown here:
l = [0, 1, 2, 3, 4, 5]
mirror = [5, 4, 3, 2, 1, 0]
I don't want to just rotate the array by 180 degrees but I actually want to replace the values. For example, my list looks like this now:
l = [2, 5]
Then l_inverted list should look like this:
l_inverted = [3, 0]
I came up with a regular way to solve it but ever since I started learning Python I've preferred list comprehensions.
l = [0, 3, 5]
mirror = [5, 4, 3, 2, 1, 0]
i = 0
for element in l:
l[i] = mirror[element]
i += 1
This actually inverts the l list. Here's my approach using a list comprehension:
l = [3, 5]
mirror = [5, 4, 3, 2, 1, 0]
for element in l:
print(element)
l = [mirror[element] if x==element else x for x in l]
This works fine.
Until:
l = [0, 3, 5]
mirror = [5, 4, 3, 2, 1, 0]
for element in l:
print(element)
l = [mirror[element] if x==element else x for x in l]
So it will replace 5 with 0, 2 with 3 and both 5s (the new one too) become 0. Obviously, I don't want it like that.
Should I stick to the working solution or is there a smooth way to solve it with list comprehensions? I'm trying to practice list comprehensions at all times but it's not fully in my brain yet. Thanks a lot.
If you want it as a list comprehension:
>> l = [0, 3, 5]
>> mirror = [5, 4, 3, 2, 1, 0]
>> l_inverted = [mirror[x] for x in l]
>> l_inverted
[5, 2, 0]
You are drowning in a spoonful of water and trying to take us with you.
You are using bad naming conventions that make your simple problem complicated to comprehend.
orig = [0, 1, 2, 3, 4, 5]
orig_rev = l[::-1]
selector = [0, 3, 5]
result = [orig_rev[i] for i in selector]
print(result ) # [5, 2, 0]
Based on your first two examples, it seems you are looking for the complement of each list value according to some base, similar to ones' complement. You could either hard-code the base as 5 or whatever, or you could assume it's the maximum number in the list and calculate it. Here's a solution for the latter:
Concept
<this complement> = <max value in list> - <this value>
Code
values = [0, 3, 2, 4, 5]
max_value = max(values)
complements = [max_value - value for value in values]
print complements
Result
[5, 2, 3, 1, 0]

Get common elements majority of lists in python

Given 4 lists, I want to get elements that are common to 3 or more lists.
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 3, 4, 5, 6]
d = [1, 2, 6, 7]
Hence, the output should be [1, 2, 3, 4].
My current code is as follows.
result1 = set(a) & set(b) & set(c)
result2 = set(b) & set(c) & set(d)
result3 = set(c) & set(d) & set(a)
result4 = set(d) & set(a) & set(b)
final_result = list(result1)+list(result2)+list(result3)+list(result4)
print(set(final_result))
It works fine, and give the desired output. However, I am interested in knowing if there is an easy way of doing this in Python, ie: are there any built in functions for this?
Using a Counter, you can do this like:
Code:
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 3, 4, 5, 6]
d = [1, 2, 6, 7]
from collections import Counter
counts = Counter(sum(([list(set(i)) for i in (a, b, c, d)]), []))
print(counts)
more_than_three = [i for i, c in counts.items() if c >= 3]
print(more_than_three)
Results:
Counter({1: 4, 2: 3, 3: 3, 4: 3, 5: 2, 6: 2, 7: 1})
[1, 2, 3, 4]
Iterate over the values in all lists to create a dict of {value: number_of_lists_the_value_appears_in}:
from collections import defaultdict
counts = defaultdict(int)
for list_ in (a, b, c, d):
for value in set(list_): # eliminate duplicate values with `set`
counts[value] += 1
Then in the second step remove all values with a count < 3:
result = [value for value, count in counts.items() if count >= 3]
print(result) # [1, 2, 3, 4]
The code below will solve the generalised problem (with n lists, and a requirement that a common element must be in at least k of them). It will work with non-hashable items, which is the main disadvantage of all the other answers:
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 2, 3, 4, 4, 5, 6]
d = [1, 2, 6, 7]
lists = [a, b, c, d]
result = []
desired_quanity = 3
for i in range(len(lists) - desired_quanity + 1): #see point 1 below
sublist = lists.pop(0) #see point 2
for item in sublist:
counter = 1 #1 not 0, by virute of the fact it is in sublist
for comparisonlist in lists:
if item in comparisonlist:
counter += 1
comparisonlist.remove(item) #see point 3
if counter >= desired_quanity:
result.append(item)
This has the disadvantage that for each element in every list, we have to check in every other list to see if it is there, but we can make things more efficient in a few ways. Also look-ups are alot slower in lists than sets (which we can't use since the OP has non-hashable items in the lists), and so this may be slow for very large lists.
1) If we require an item to be in k lists, we don't need to check each item in the last k-1 lists, as we would have already picked it up whilst searching through the first k lists.
2) Once we have searched through a list, we can discard that list, since any items in the just-searched-list that might contribute to our final result, will again already have been dealt with. This means that with each iteration we have fewer lists to search through.
3) When we have checked if an item is in enough lists, we can remove that item from the list, which means not only is the number of lists getting shorter as we proceed, the lists themselves are getting shorter, meaning quicker lookups.
As an aftersort, if we the original lists happen to be sorted beforehand, this might also help this algorithm work efficiently.
create a dictionary of counts and filter out those with count less than 3

Creating a new list when before it reaches a number

How do I create a new list that contains sublists of ints but the way of divide it is when the next number is the minimun (or equal to the first value founded)?
For example
List1=[1,2,3,4,5,1,2,3,4,1,2,3,4,5,6]
The output that I am looking for is shown below:
Complete_List=[[1,2,3,4,5],[1,2,3,4],[1,2,3,4,5,6]]
I tried looping through the list and appending it when the value is greater than 1 . However it will not work as it doesn't create another list inside it.
Do I have to right a regex for this problem?
Some guidance would be really helpful.
Thank you
Here's something that will split a generic iterable on a given value.
def split_on_value(iterable, split_value):
iterator = iter(iterable)
outer, inner = [], [next(iterator)]
for value in iterator:
if value == split_value:
outer.append(inner)
inner = []
inner.append(value)
outer.append(inner)
return outer
value_list = [1, 2, 3, 4, 5, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
print split_on_value(value_list, 1)
# [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
print split_on_value(value_list, 3)
# [[1, 2], [3, 4, 5, 1, 2], [3, 4, 1, 2], [3, 4, 5, 6]]
A vanilla, straightforward, CS101 solution. Though, possibly the most efficient one, because it scans the list exactly once. It also does not assume that segments begin with 1.
fragment = []
result = []
prev = List1[0] - 1 # Preset the previous element marker
for n in List1:
if n > prev:
fragment.append(n)
else:
result.append(fragment)
fragment = [n]
prev = n
result.append(fragment)
#[[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
First you search for the 1's, or whatever your condition is, and get the indices within the list. Don't forget to append the len(list) to include the last segment.
idx = [i for i, l in enumerate(List1) if l == 1] + [len(List1)]
Optional, if you want the beginning end of the List. That is, you do not know if there will be a 1 always at index 0.
idx = [0] + idx if idx[0] != 0 else idx
Then, split the list at those indices you found.
complete_list = [List1[ind1:ind2] for ind1, ind2 in zip(idx[:-1], idx[1:])]
and the result:
[[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
You can try this to split at every instance of 1:
List1=[1,2,3,4,5,1,2,3,4,1,2,3,4,5,6]
print [map(int, list("1"+i)) for i in ''.join(map(str, List1)).split("1")][1:]
By mapping over List1 with the string function, we can then join all the numbers in the list into one large string. From there, the algorithm splits itself at each instance of one, creating a list containing the new strings of digits. from there, the code maps the integer function over a list created of the strings and appending 1 at the front of the string to make up for the lost 1 when it originally split, creating a list within a list.

make new python lists with indexes from items in a single list

I want to take a list, e.g. [0, 1, 0, 1, 2, 2, 3], and make a list of lists of (index + 1) for each of the unique elements. For the above, for example, it would be [[1, 3], [2, 4], [5, 6], [7]].
Right now my solution is the ultimate in clunkiness:
list_1 = [0, 1, 0, 1, 2, 2, 3]
maximum = max(list_1)
master = []
for i in range(maximum + 1):
temp_list = []
for j,k in enumerate(list_1):
if k == i:
temp_list.append(j + 1)
master.append(temp_list)
print master
Any thoughts on a more pythonic way to do this would be much appreciated!
I would do this in two steps:
Build a map {value: [list, of, indices], ...}:
index_map = {}
for index, value in enumerate(list_1):
index_map.setdefault(value, []).append(index+1)
Extract the value lists from the dictionary into your master list:
master = [index_map.get(index, []) for index in range(max(index_map)+1)]
For your example, this would give:
>>> index_map
{0: [1, 3], 1: [2, 4], 2: [5, 6], 3: [7]}
>>> master
[[1, 3], [2, 4], [5, 6], [7]]
This implementation iterates over the whole list only once (O(n), where n is len(list_1)) whereas the others so far iterate over the whole list once for each unique element (O(n*m), where m is len(set(list_1))). By taking max(d) rather than max(list_1) you only need to iterate over the length of the unique items, which is also more efficient.
The implementation can be slightly simpler if you make d = collections.defaultdict(list).
list_1 = [0, 1, 0, 1, 2, 2, 3]
master = []
for i in range(max(list_1)+1):
master.append([j+1 for j,k in enumerate(list_1) if k==i])
master = [[i+1 for i in range(len(list_1)) if list_1[i]==j] for j in range(max(list_1)+1)]
It is just the same that your current code, but it uses list comprehension which is often a quite good pythonic way to solve this kind of problem.

Categories

Resources