0

I have a list of dictionaries, like the one below:

l = [ { "a": 10, "b": 4, "c": 6 },
      { "a": 10, "b": 6, "c": 8 },
      { "a": 13, "b": 3, "c": 9 },
      { "a": 12, "b": 5, "c": 3 },
      { "a": 11, "b": 7, "c": 1 } ]

Now, I want to slice it and have a list only with dictionaries where the key a has value 10, but removing the key a from the dictionary. Like the list below:

nl = [ { "b": 4, "c": 6 },
       { "b": 6, "c": 8 } ]

I can do this by processing l twice:

l[:] = [d for d in l if d.get("a") == 10]
nl = []
for c in l:
    del c["a"]
    nl.append(c)

Is there a more straightforward way to accomplish this?

dclobato
  • 43
  • 7

6 Answers6

7

There may be a better way, but you can do a nested dict comprehension:

[{k:d[k] for k in d if k!="a"} for d in l if d.get("a") == 10]
Noam
  • 550
  • 2
  • 11
2

I think I've got a pretty cool one-liner that should be very fast and works whether or not a is in the dictionary. It relies on the fact that popping from a dictionary returns the value of the key (or none if key is not present) as well as removes the entry. Note that it does mutate l though.

l = [ { "a": 10, "b": 4, "c": 6 },
      { "a": 10, "b": 6, "c": 8 },
      { "a": 13, "b": 3, "c": 9 },
      { "a": 12, "b": 5, "c": 3 },
      { "q": 12, "b": 5, "c": 3 },
      { "a": 11, "b": 7, "c": 1 } ]

ret = [d for d in l if "a" in d.keys() and d.pop("a") == 10]
print ret

>>[{'c': 6, 'b': 4}, {'c': 8, 'b': 6}]
mitoRibo
  • 4,468
  • 1
  • 13
  • 22
1

Maybe you can use the functions filter and map for readability and immutability.

Example (I didn't test it):

l = [ { "a": 10, "b": 4, "c": 6 },
  { "a": 10, "b": 6, "c": 8 },
  { "a": 13, "b": 3, "c": 9 },
  { "a": 12, "b": 5, "c": 3 },
  { "a": 11, "b": 7, "c": 1 } ]

l_filtered = filter(lambda x: x.get('a') == 10, l)
l_filtered_without_a = map(lambda x: {'b': x.get('b'), 'c': x.get('c')}, l_filtered)
1

Similar to This question on SO for element removal from dictionary. The rest is straightforward.

[ {i:elem[i] for i in elem if i!="a"} for elem in l if elem["a"] == 10]

Returns

[{'b': 4, 'c': 6}, {'b': 6, 'c': 8}]

Community
  • 1
  • 1
Rahul Madhavan
  • 304
  • 3
  • 10
0

Seems to me that you're working too hard to make this a one-liner. Create two empty lists and append each dict to one or the other depending on the value of the a key.

>>> l = [ { "a": 10, "b": 4, "c": 6 },
...       { "a": 10, "b": 6, "c": 8 },
...       { "a": 13, "b": 3, "c": 9 },
...       { "a": 12, "b": 5, "c": 3 },
...       { "a": 11, "b": 7, "c": 1 } ]
>>> l1 = []
>>> nl = []
>>> for d in l:
...    targetList = l1 if d.get('a') == 10 else nl
...    targetList.append(d)
>>> l1
[{'a': 10, 'c': 6, 'b': 4}, {'a': 10, 'c': 8, 'b': 6}]
>>> nl
[{'a': 13, 'c': 9, 'b': 3}, {'a': 12, 'c': 3, 'b': 5}, {'a': 11, 'c': 1, 'b': 7}]
>>>
bgporter
  • 35,114
  • 8
  • 59
  • 65
0

It can be done in a one-liner with map/filter

res = [elem for elem in map(lambda x:{k:v for k,v in x.items() if k != 'a'}, [e for e in filter(lambda y:y.get('a') != 10,a)])]
Kostas Pelelis
  • 1,322
  • 9
  • 11