8

What's a more pythonic or efficent way of removing the last element of each list, in a dictionary of lists?

For example, take this:

listDict = {'tom': [-2,10,2,-8], 'sam': [-9,-10,-10,-7]}

And transform it into this:

listDict = {'tom': [-2,10,2], 'sam': [-9,-10,-10]}

This is what I'm currently doing:

new = {}
for t,item in listDict.iteritems():
    new[t] = [item[0], item[1], item[2]]
listDict= new
Zach
  • 4,624
  • 13
  • 43
  • 60

5 Answers5

8

I would use a dictionary comprehension:

new_dict = {key: value[:-1] for key, value in listDict.items()}

For older Python versions you'll have to use the dict() constructor:

new_dict = dict((key, value[:-1]) for key, value in listDict.items())
Blender
  • 289,723
  • 53
  • 439
  • 496
  • 2
    +1, great answer. One thing: `{}.iteritems()` goes away soon, and `{}.items()` works great in Py 2.X and Py 3.X. Consider using that instead. – dawg Aug 11 '12 at 05:22
  • @drewk +1 for you. Didn't know iteritems is going away. – User007 Aug 11 '12 at 05:40
8

Blender's answer grows very inefficient as the lengths of the lists increases, compared to this solution:

for k, v in listDict.items():
    v.pop()

For your example listDict, the difference is not big, just 27%. But using a dict with 100 keys and lists of length from 50 to 100, and popping 50 of them, the dict comprehension method takes more than 12 times longer. This is because this solution modifies the existsing lists instead of creating copies of each list every time.

I'm afraid that there is no one-liner version of this, unless you're cheating. The reason I mention this at all is to prevent that some dofus feels compelled to point it out in comments. Please don't use dict/list comprehensions for side effects.

Of course you can do it on one line as follows, but PEP8 says "rather not":

for k, v in listDict.items(): v.pop()
Community
  • 1
  • 1
Lauritz V. Thaulow
  • 49,139
  • 12
  • 73
  • 92
  • +1 great answer and review of other alternative answers, with good links to style guides. – Zach Aug 11 '12 at 06:21
2

If you want in place modification then this alternative does not require creating a new dictionary, or any other re-assignment.

listDict = {'tom': [-2,10,2,-8], 'sam': [-9,-10,-10,-7]}
for i in listDict.values():
    i.pop()

and it doesn't require creating new lists. Remember lists are mutable.

If you do want new dicts and lists then the previous response is better.

Tim Hoffman
  • 12,976
  • 1
  • 17
  • 29
1

Just to keep score:

def f1():
    listDict = {'tom': [-2,10,2,-8], 'sam': [-9,-10,-10,-7]}
    return {key: value[:-1] for key, value in listDict.items()}

def f2():
    listDict = {'tom': [-2,10,2,-8], 'sam': [-9,-10,-10,-7]}
    for k, v in listDict.items():
        v.pop()    

    return listDict    


cmpthese.cmpthese([f1,f2])       

Prints:

   rate/sec    f1     f2
f1  571,487    -- -23.5%
f2  746,966 30.7%     --
dawg
  • 98,345
  • 23
  • 131
  • 206
0

If you are able to use pandas, given this dict:

listDict = {'tom': [-2,10,2,-8], 'sam': [-9,-10,-10,-7]}

Remove the last element like so:

import pandas 
df = pandas.DataFrame(data=listDict)
new = df[:-1]
print(new)

Which produces:

   tom  sam
0   -2   -9
1   10  -10
2    2  -10

If you want it as a dict then:

print(new.to_dict('list'))

Which produces:

{'tom': [-2, 10, 2], 'sam': [-9, -10, -10]}
Sooth
  • 2,834
  • 23
  • 26