Multikey Dict?

D

David Rasmussen

If I have a collection of dicts like:

john = {'id': 1, 'name': "John Cleese", 'year': 1939}
graham = {'id': 2, 'name': "Graham Chapman", 'year': 1941}

I could store all of them in a list. But for easy lookup, I might store
all these in a dict instead, like

people = {'1': john, '2': graham}

or maybe

people = {'John Cleese': john, 'Graham Chapman': graham}

or whatever key I might choose. Now, first of all, it seems a bit
annoying that I have to keep that redundant data in the second dict that
is already in the individual dicts within people. Secondly (and this is
my question), it is annoying that I have to choose one of several
unambiguous keys as a key.

I would like to be able to say:

people['1'].year

in some case and in other cases I want to say

people['John Cleese'].year

That is, sometimes I have the name at hand and would like to look up
data based on that. Other times, I have the ID at hand and would like to
look up data based on that instead.

Also, I would like if I didn't have to keep the key data both in the
dict of dicts and in the dicts :)

If I could just say to Python: john and graham (and ...) are all a part
of a "superdict" and either their id or their name can be used as keys.

Can I do that somehow?

/David
 
S

Sam Pointon

If I could just say to Python: john and graham (and ...) are all a part
of a "superdict" and either their id or their name can be used as keys.
Can I do that somehow?

Sure you can. There are two obvious ways to do this - enlist the aid of
a Superdict (or similar) class to take care of the details. A simple
implementation would be:

class Superdict(object):

def __init__(self, by_id, by_name):
self.by_id = by_id
self.by_name = by_name

def __getitem__(self, value):
if value.isdigit():
return self.by_id[value]
else:
return self.by_name[value]


The alternative is to use some sort of database for storing these
values. It doesn't really matter which (hell, you could even reinvent a
relational wheel pretty simply in Python), as any DB worth its salt
will do exactly what you need.

Hope that helps,
-Sam
 
M

Mike Meyer

David Rasmussen said:
If I have a collection of dicts like:
john = {'id': 1, 'name': "John Cleese", 'year': 1939}
graham = {'id': 2, 'name': "Graham Chapman", 'year': 1941}
I could store all of them in a list. But for easy lookup, I might
store all these in a dict instead, like
people = {'1': john, '2': graham}
or maybe
people = {'John Cleese': john, 'Graham Chapman': graham}

or whatever key I might choose. Now, first of all, it seems a bit
annoying that I have to keep that redundant data in the second dict
that is already in the individual dicts within people.

I don't see any redundant data in the dictionary. The *keys* might be
redundant, but that's because of the way you chose to store them.
Secondly (and this is my question), it is annoying that I have to
choose one of several unambiguous keys as a key.

So use them all. No big deal.
I would like to be able to say:
people['1'].year
in some case and in other cases I want to say
people['John Cleese'].year

This syntax looks like the values of the dictionary are objects, and
you're referencing attributes as opposed to looking up values in a
dictonary.
Also, I would like if I didn't have to keep the key data both in the
dict of dicts and in the dicts :)

Then don't. Dictionary keys are references objects. Make sure you
reference use the same object in both dictionaries.
If I could just say to Python: john and graham (and ...) are all a
part of a "superdict" and either their id or their name can be used as
keys.

Can I do that somehow?

It's easy enough to build your superdict class:

# Untested code
class SuperDict(dict):
def __init__(self, *keys):
dict.__init__(self)
self.keys = *keys

def __setitem__(self, key, item):
# Set the explicit key
dict.__setitem__(self, key, item)

# Set the implicit keys
for k in self.keys:
dict.__setitem__(self, item[k], item)

I think that gives you the behavior you want, except you have to use
the index syntax for both indices, instead of beinng able to use the
attribute syntax for the object returned by the dictionary.

<mike
 
B

Bruno Desthuilliers

David Rasmussen a écrit :
If I have a collection of dicts like:

john = {'id': 1, 'name': "John Cleese", 'year': 1939}
graham = {'id': 2, 'name': "Graham Chapman", 'year': 1941}

I could store all of them in a list. But for easy lookup, I might store
all these in a dict instead, like

people = {'1': john, '2': graham}
> or maybe
>
> people = {'John Cleese': john, 'Graham Chapman': graham}

You can use integers as keys in a dict. And building an index is not
that difficult:

peoples = [
{'id': 1, 'name': "John Cleese", 'year': 1939},
{id': 2, 'name': "Graham Chapman", 'year': 1941},
]

peoples_name_idx = dict((p['name'], p) for p in peoples)
peoples_id_idx = dict((p['id'], p) for p in peoples)

which generalize to:
def mk_index(index_field, records):
return dict((rec[index_field], rec) for rec in records)

(snip)
would like to be able to say:
people['1'].year

in some case and in other cases I want to say
people['John Cleese'].year

This should be:
people['John Cleese']['year']

(If you want transform your dicts to objects so you use dotted notation,
there are recipes for this in the Python Cookbook.)
If I could just say to Python: john and graham (and ...) are all a part
of a "superdict" and either their id or their name can be used as keys.

Can I do that somehow?

Writing a dict-like object is quite easy. Look for UserDict, or directly
subclass Dict.

Now, how could the "superdict" implementation decide which key to use ?

Here, you have a numeric 'id' and a string 'name', so you could test on
it in __getitem__, but what if you want to use 2 different string keys?
Computers are usually very bad at guessing, that's why we are
programming them.



My 2 cents
 
B

Bruno Desthuilliers

Sam Pointon a écrit :
If I could just say to Python: john and graham (and ...) are all a part
of a "superdict" and either their id or their name can be used as keys.

Can I do that somehow?


Sure you can. There are two obvious ways to do this - enlist the aid of
a Superdict (or similar) class to take care of the details. A simple
implementation would be:

class Superdict(object):

def __init__(self, by_id, by_name):
self.by_id = by_id
self.by_name = by_name

def __getitem__(self, value):
if value.isdigit():
return self.by_id[value]
else:
return self.by_name[value]

What if the OP now wants non numeric id fields ?
 
S

Scott David Daniels

David said:
If I have a collection of dicts like:

john = {'id': 1, 'name': "John Cleese", 'year': 1939}
graham = {'id': 2, 'name': "Graham Chapman", 'year': 1941}
If these are all like that, I prefer:
john = dict(id=1, name="John Cleese", year=1939}
graham = dict{id-2, name="Graham Chapman", year=1941}

--Scott David Daniels
(e-mail address removed)
 
B

Bengt Richter

If I have a collection of dicts like:

john = {'id': 1, 'name': "John Cleese", 'year': 1939}
graham = {'id': 2, 'name': "Graham Chapman", 'year': 1941}
Why do you use dicts for what looks like fixed-format records? Why not just tuples? E.g.,
john = (1, "John Cleese", 1939)
graham = (2, "Graham Chapman", 1941)

I could store all of them in a list. But for easy lookup, I might store
all these in a dict instead, like

people = {'1': john, '2': graham}

or maybe

people = {'John Cleese': john, 'Graham Chapman': graham}

or whatever key I might choose. Now, first of all, it seems a bit
annoying that I have to keep that redundant data in the second dict that
ISTM you are confusing equivalent references with redundant data. The
people dict does not "contain" its own copy of john and graham objects,
it just refers to them, and you get to look up the reference by key name.
is already in the individual dicts within people. Secondly (and this is
my question), it is annoying that I have to choose one of several
unambiguous keys as a key.
Keys in the same dict have to be unambiguous, or how could you retrieve
an associated value?
I would like to be able to say:

people['1'].year

in some case and in other cases I want to say

people['John Cleese'].year

That is, sometimes I have the name at hand and would like to look up
data based on that. Other times, I have the ID at hand and would like to
look up data based on that instead.
No prob. So long as no one has a name like '1' or an id like 'John Cleese'
you can keep both '1':john and 'John Cleese':john key:value associations
in the same dict. Referring to the same value with different keys is no prob.

Of course, using tuples for john and graham you can't get year as an attribute.
Recalling from above
john = (1, "John Cleese", 1939)

you'd have to write
people['1'][2]
or
people['John Cleese'][2]

Or you could define some handy mnemonic constants to match the tuple, e.g.,
ID, NAME, YEAR = range(3)

and then you could write
people['1'][YEAR]
or
people['John Cleese'][YEAR]

Or you could define an object giving you the attribute access you wanted.
Not as efficient spacewise though, unless you define __slots__.

Of course, you can use dicts for john and graham if you need the ability
to add arbitrary fields. If this happens rarely, you could conceivably gain
efficiency in storage by using slots for the normal data and a single slot
initialized to None for extra data, and then create a dict for that slot
only if you need to extend with new field names.
Also, I would like if I didn't have to keep the key data both in the
dict of dicts and in the dicts :)
IIUC I guess you don't have to.
If I could just say to Python: john and graham (and ...) are all a part
of a "superdict" and either their id or their name can be used as keys.

Can I do that somehow?
Sure. What API would you like for your superdict?
Defining requirements is usually the hardest part, because newbies
often imagine limitations that don't exist in Python ;-)

Others will have proposed many variants by now, I assume ;-)

Regards,
Bengt Richter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,130
Latest member
MitchellTe
Top