Alex Kliorin
Alex Kliorin

Reputation: 27

How to delete a dictionary from a list based on values from another dictionary in a list?

I've spent about 6 hours on this, so I feel justified in asking a question. :)

My problem

list1 = [
       {extension: 1000, id: 1}
       {extension: 1001, id: 1}
       {extension: 1000, id: 2}
        ]

list2 = [
       {Stationextension: 1000, id: 1, name: bob}
       {Stationextension: 1001, id: 1, name: sal}
       {Stationextension: 1000, id: 2, name: cindy}
        ]

My pseudo code is this.

Delete list2[d] (whole dictionary from list of dicts) where list1[d]['extension'] == list2[d]['Stationextension'] AND id == id.

List 1 is much smaller and has to stay unchanged relative to list 2 if that matters.

Using Python3.3, I have something like

[x for x in list2 if (list1[x]['stationExtension'] == x.get('extension'))]

Thanks !

Upvotes: 0

Views: 118

Answers (4)

abarnert
abarnert

Reputation: 365697

The right way to do this is to use the right data structures for the job. Instead of figuring out how to search a list based on a key, use a dict with that key. For example:

dict1 = {x['extension']: x for x in list1}

Now, you can just write this:

[x for x in list2 if x['stationExtension'] in dict1}

Elsewhere in your question, it seems like you wanted to use the extension and the id, not just the extension, as a key. But that's just as easy; you can use a tuple:

dict1 = {(x['extension'], x['id']): x for x in list1}

[x for x in list2 if (x['stationExtension'], x['id']) in dict1}

If you want to know why your code doesn't work… let's look at it:

[x for x in list2 if (list1[x]['stationExtension'] == x.get('extension'))]

list1[x] is trying to use the dict as an index, which doesn't mean anything. What you really want to say is "if, for any element of list1, element['stationExtension'] == x.get('extension')". And you can translate that almost directly to Python:

[x for x in list2 
 if any(element['stationExtension'] == x.get('extension')
        for element in list1)]

And of course to add the and element['id'] == x['id'] onto the end of the condition.

But again, the dict is a better solution. Besides being a whole lot simpler to read and write, it's also more efficient. Searching for any matches in a list requires checking each element in the list; searching for any matches in a dict just requires hashing the key and looking it up in a hash table. So, this way takes O(NM) time (where N is the length of list1, and M is the length of list2), while the dict only takes O(M) time.

Upvotes: 1

Anshu Dwibhashi
Anshu Dwibhashi

Reputation: 4675

Python 2.7:

removelist=[]
for i,x in enumerate(list2):
    if x['Stationextension'] == list1[i]['extension'] and x['id'] == list1[i]['id']:
        removelist+=[i]

And once you've decided what to delete:

for x in removelist:
    del list1[x]

Upvotes: 0

jsbueno
jsbueno

Reputation: 110261

Assuming that you want to delete items from list2 with the same value in "Stationextention" on list2 and "extention" on list 1 - You better first create a set with all values to delete - that will avoid a linear search at each time you want to check if an entry will stay:

indexes = set(item["extention"] for item in list1)

And - one thing that happens is that you usually can't iterate over a list and delete items from it while at it:

new_list2 = []
for item in list2:
    if item["Stationextention"] not in indexes: 
         new_list2.append(item)

list2 = new_list2

Upvotes: 0

John Spong
John Spong

Reputation: 1381

list2 = [x for x in list2 if {'extension': x['Stationextension'], 'id': x['id']} not in list1]

or, if you know that the lengths and indexes will match:

list2 = [y for x,y in itertools.izip(list1, list2) if x['extension'] == y['StationExtension'] and x['id'] == y['id']]

Upvotes: 1

Related Questions