Boltian
Boltian

Reputation: 13

Python : List + lambda behaviour

I have a list of dictionaries. Each dictionary in the list has the same keys but each has different values, e.g.:

my_list = [
  {key1: 100, key2: 110, key3: 120},
  {key1: 200, key2: 210, key3: 220},
  {key1: 300, key2: 310, key3: 320}]

I used the following to replace key2 with a new key new_key2:

list(map(lambda x: x.update({"new_key2": x.pop("key2")}), my_list))

The code works and my_list is updated correctly, but I cannot understand the following behavior:

my_list = list(map(lambda x: x.update({"new_key2" : x.pop ("key2")}), my_list))

In this case my_list returned the following values:

my_list[0] = None
my_list[1] = None
my_list[2] = None

Why? Shouldn't 'list + map' functions retrieve me a list?

Upvotes: 1

Views: 494

Answers (3)

Alexander
Alexander

Reputation: 109636

The result of an update statement is None, hence why the returned result is [None, None, None]. There is no need to call list on the map function. This function is updating each item in mylist inplace.

I believe the following code is more pythonic and also handles the case when there is no key2. The variable mylist is modified inplace, so there is no need to reassign it to a list. The approach below is slightly more memory efficient compared to a reassigning the result of a list comprehension.

for d in mylist:
    try:
        d['new_key2'] = d.pop('key2')
    except KeyError:
        pass  # no 'key2'

>>> mylist
[{'key1': 100, 'key3': 120, 'new_key2': 110},
 {'key1': 200, 'key3': 220, 'new_key2': 210},
 {'key1': 300, 'key3': 320, 'new_key2': 310}]

If you prefer to use the map method (it is faster in this case), I'd recommend the following:

map(lambda x: 
    x.update({"new_key2": x.pop("key2")}) 
    if 'key2' in x else None, mylist)

There is no need to assign the result, as it is just a list of None values.

Upvotes: 2

Patrick Haugh
Patrick Haugh

Reputation: 61032

So there are two ways to do this. The first modifies the existing dictionaries (and returns a reference to the dictionary so you don't have to deal with None)

def replace_key(old_key, new_key, d):
    d[new_key] = d.pop('old_key')
    return d

mylist = [replace_key('key2', 'newkey2', d) for d in mylist]

The second is a more pure functional approach without side effects (I wouldn't recommend this for real applications, it's much less efficent)

mylist = [{k : v if k != 'key2' else 'newkey2': v for k, v in d.items()} for d in mylist]

Upvotes: 0

rma
rma

Reputation: 1958

Your lambda function does not return anything, it just updates the list. Look at this block of code.

In [15]: mylist = [
  {'key1' : 100, 'key2' : 110, 'key3' : 120},
  {'key1' : 200, 'key2' : 210, 'key3' : 220},
  {'key1' : 300, 'key2' : 310, 'key3' : 320}]

In [16]: list(map(lambda x: x.update({"mewkey2" : x.pop ("key2")}), mylist))
Out[16]: [None, None, None]

In [17]: mylist
Out[17]:
[{'key1': 100, 'key3': 120, 'mewkey2': 110},
 {'key1': 200, 'key3': 220, 'mewkey2': 210},
 {'key1': 300, 'key3': 320, 'mewkey2': 310}]

If you want to have a new variable corresponding to this new list, do something like

newlist = mylist[::]
list(map(lambda x: x.update({"mewkey2" : x.pop ("key2")}), newlist))

Upvotes: 2

Related Questions