Benjamin Pichl
Benjamin Pichl

Reputation: 39

Append items to a list according to occurences in another list

so I have a list:

my_list1 = ["p1", "p2", "p4", "p1"]

and I have another list with sublists:

my_list2 = [[1,"p1"], [1,"p2"], [1,"p3"], [1,"p4"], [2, "p1"], [2, "p2"], [2, "p3"], [2, "p4"]]

Now what I want to do is the following: I want to iterate through my_list1 and then through my_list2 and create a new list that contains the next occurence of each element of my_list1 in my_list2.

I.e. what I want to obtain is:

new_list = [[1, "p1"], [1, "p2"], [1, "p4"], [2, "p1"]]

I have alrealy tried the following:

    new_list = []
    for i in my_list1:
        for j in my_list2:
            if i in j[1]:
                new_list.append(j)

Which gives me

new_list = [[1,"p1"], [1,"p2"], [1,"p4"], [2, "p1"], [2, "p2"],  [2, "p4"]]

So, again, what I need is that each iteration appends ONLY THE NEXT occurence of an item in my_list2.

I'm new to Python, so please be gentle. I'm very thankful for any suggestions!

Upvotes: 1

Views: 87

Answers (4)

pault
pault

Reputation: 43494

There are a few problems with your code, but the logic is basically correct. You just need to do 2 things differently:

1) Break out of the loop when you append.

2) Remove the item that was appended (so you don't append it again later)

Here is one approach that you can take:

# create a copy of my_list2 because calling pop will mutate the list
temp_list2 = [x for x in my_list2]

new_list = []
for i,x in enumerate(my_list1):
    for j,y in enumerate(temp_list2):
        if x == y[1]:
            new_list.append(temp_list2.pop(j))
            break
print(new_list)
#[[1, 'p1'], [1, 'p2'], [1, 'p4'], [2, 'p1']]

Also, don't use in to compare the values, use == instead.


Here is an alternative, more efficient approach using collections.Counter:

from collections import Counter
list1_counter = Counter(my_list1)

new_list = []
for (value, key) in my_list2:
    if key in list1_counter and list1_counter[key] > 0:
        new_list.append([value, key])
        list1_counter[key] -= 1
print(new_list)
#[[1, 'p1'], [1, 'p2'], [1, 'p4'], [2, 'p1']]

You build a Counter to count the occurrence of each "key" in my_list1. Then you iterate over my_list2 and check to see if the key exists in the counter and the count is greater than 0. If so, add the item to the list and decrease the counter.

Upvotes: 3

Eugene Yarmash
Eugene Yarmash

Reputation: 149736

You could use collections.defaultdict to group the elements in my_list2 by second item, then consume them while iterating over my_list1:

>>> from collections import defaultdict, deque
>>> d = defaultdict(deque)
>>> for elem in my_list2:
...     d[elem[1]].append(elem)
>>> [d[elem].popleft() for elem in my_list1]
[[1, 'p1'], [1, 'p2'], [1, 'p4'], [2, 'p1']]

Using a deque instead of a list allows for efficient pops from the left side.

Upvotes: 2

Kenstars
Kenstars

Reputation: 660

Isnt it a bit straightforward for you , you've stopped it one step early.

my_list1 = ["p1", "p2", "p4", "p1"]
my_list2 = [[1,"p1"], [1,"p2"], [1,"p3"], [1,"p4"], [2, "p1"], [2, "p2"], [2, "p3"], [2, "p4"]]
choice = 0
final_list = []
list_1_length = len(my_list1)
for each_element in my_list2:
    if each_element[1] == my_list1[choice]:
       final_list.append(each_element)
       choice += 1
       if choice == list_1_length:
          break

Upvotes: 0

Alekos
Alekos

Reputation: 378

try this:

new_list = []
tmp_list = my_list2.copy() #only to preserve my_list2
    for i in my_list1:
        index = 0
        for j in tmp_list :
            if i in j[1]:
                new_list.append(tmp_list.pop(index))
                break
            index += 1

Upvotes: 1

Related Questions