expression form of one-to-many dict?

Robert Brewer fumanchu at amor.org
Fri Dec 17 17:07:57 EST 2004


Steven Bethard wrote:
> So I end up writing code like this a fair bit:
> 
> map = {}
> for key, value in sequence:
>      map.setdefault(key, []).append(value)
> 
> This code basically constructs a one-to-many mapping -- each 
> value that 
> a key occurs with is stored in the list for that key.
> 
> This code's fine, and seems pretty simple, but thanks to generator 
> expressions, I'm getting kinda spoiled. ;)  I like being able to do 
> something like the following for one-to-one mappings:
> 
>      dict(sequence)
> 
> or a more likely scenario for me:
> 
>      dict((get_key(item), get_value(item) for item in sequence)
> 
> The point here is that there's a simple sequence or GE that I 
> can throw 
> to the dict constructor that spits out a dict with my 
> one-to-one mapping.
> 
> Is there a similar expression form that would spit out my one-to-many 
> mapping?

Given:

class Chain(dict):
    """Behaves like a dictionary, but setting a value adds it to a list
    instead of replacing the existing value.
    
    Retrieving a value always returns the first such value. If you want
    all such values, call Chain.chain(key) instead.
    
    Deleting an item removes the entire list for that key.
    """
    
    def __init__(self, initialValues={}):
        for key, value in initialValues.items():
            self[key] = value
    
    def __getitem__(self, key):
        return dict.__getitem__(self, key)[0]
    
    def __setitem__(self, key, value):
        self.setdefault(key, []).insert(0, value)
    
    def get(self, key, default=None):
        try:
            return dict.__getitem__(self, key)[0]
        except KeyError:
            return default
    
    def update(self, newDict):
        for key, value in newDict.items():
            self[key] = value
    
    def items(self):
        return [(key, val[0]) for key, val in dict.items(self)]
    
    def values(self):
        return [val[0] for val in dict.values(self)]
    
    def iteritems(self):
        for key, val in dict.iteritems(self):
            yield (key, val[0])
    
    def itervalues(self):
        for val in dict.itervalues(self):
            yield val[0]
    
    def chain(self, key):
        """Return all values of a given key in a list."""
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            return []
    
    def iterchains(self):
        """C.iterchains() -> an iterator over the (key, chain) pairs of
C."""
        return dict.iteritems(self)


...the one-liners flow effortlessly. ;)


Robert Brewer
MIS
Amor Ministries
fumanchu at amor.org



More information about the Python-list mailing list