invert dictionary with list &c

Des Small des.small at bristol.ac.uk
Thu Nov 27 13:24:39 CET 2003


anton muhin <antonmuhin.REMOVE.ME.FOR.REAL.MAIL at rambler.ru> writes:

> Des Small wrote:
> 
> > Lately I have found myself using a pattern to make new dictionaries
> > quite often, by which I mean twice:
> > def invert(d):
> >     nd = {}
> >     [nd.setdefault(val, []).append(key) for k, v in d]
> >     return nd
> > def count(l):
> >     d = {}
> >     [d.setdefault(w, 0) += 1 for w in l]
> >     return d

As another poster noted, this latter is broken.

> > Is this the pythonic way to do such things?  Ideally I'd like to
> > write
> > them as one liners, but I can't see how.
> > Des
> 
> Most pythonic way IMHO would be:
> 
> def invert(d):
>      nd = {}
>      for k, v in d.iteritems():
>          nd[v] = nd.get(k, []) + [k]
>      return nd
> 
> def count(l):
>      d = {}
>      for e in l:
>          d[e] = d.get(e, 0) + 1
>      return d

I was anxious to extract the common pattern, if possible, but these
are clean enough that it seems slightly perverse.

[...]

> However, if you insist on one-liners, I can suggest some ;):
> 
> def count(l):
>    return reduce(
>      lambda d, e: (d.update(dict([(e, d.get(e, 0) + 1)])), d)[1],
>      l, {}
>    )
> 
> def invert(d):
>    return reduce(
>      lambda d, (k, v): (d.update(dict([(v, d.get(v, []) + [k])])), d)[1],
>      d.iteritems(), {}
>    )
> 
> (given in several lines, but can be written in one)

This, however, has given me ideas.  It was never concision I wanted
but rather redundancy elimination, and the pattern I wanted _can_ be
written:

def dict_cons(iter, func, default):      
    def process_element(d, (k, v)):
        val = d.get(k, default)
        d.update(dict([[k, func(v, val)]]))
        return d
    return reduce(process_element, iter, {})

Which is not to say that it should be, of course.  
Whereupon, we can say:

def count(l):
    def pair_pad(l): return [(e, ()) for e in  l]
    return dict_cons(pair_pad(l), lambda k,d: d+1, 0) 

def invert(d):
    def invertitems(l): for k,v in l: yield v,k
    def addtolist(k, l): return l+[k]
    return dict_cons(invertitems(d.iteritems()),
                     addtolist, [])

Perhaps I'm terminally unpythonic, but I quite like these.

> count = lambda l: reduce(
>      lambda d, e: (d.update(dict([(e, d.get(e, 0) + 1)])), d)[1],
>      l, {}
> )
> 
> :)

If Python's lambda wasn't so broken I'd use it all the time, for sure.

Des
-- 
"[T]he structural trend in linguistics which took root with the
International Congresses of the twenties and early thirties [...] had
close and effective connections with phenomenology in its Husserlian
and Hegelian versions." -- Roman Jakobson




More information about the Python-list mailing list