help - iter & dict

Simon Forman rogue_pedro at yahoo.com
Thu Aug 3 17:20:29 EDT 2006


aking at mappi.helsinki.fi wrote:
> Dear Python people,
>
> im a newbie to python and here...so hello!

Hi Ali, and welcome.

> Im trying to iterate through values in a dictionary so i can find the
> closest value and then extract the key for that value....what ive done so far:
>
> def pcloop(dictionary, exvalue):
>         z = dictionary.itervalues()
>         y = z - exvalue
>         v = (y*y)**1/2
>         if v < 0.001:
>             u = dictionary.get[z]
>         return u
>
>
> ive been working off a couple of books and this is the best i can get it in
> short time. I was trying to define a function (its my first!) so that i
> could apply to several 'dictionary's and 'exvalue's. The best ive been able
> to come up with is iterating over the dictionary values, subtracting the
> exvalue, squaring then root squaring to render positive and applying an
> error (0.001) which is not the ideal solution i want. Is there any easy way
> to iterate through dictionary values and return the key for the minimum. Or
> can someone tell me where im going wrong with this def & loop.
>
> regards all
>
> Ali

You're doing many interesting things wrong here.  :-)  I'm going to
take them slightly out of order.

First, very ingenious way to get the absolute value of a number, but
there are a few issues with this that you should know about.

For instance, just as multiplication and division take precedence over
addition and subtraction, the "power" operator ** take precedence over
division,  so what you're really doing above is

((y*y)**1)/2

rather than

(y*y)**(1/2)

However, the above still wouldn't work correctly because of the way
python handles integer division.  1/2 == 0 in python, try it at the
interactive prompt and you'll see.

So, you'd have to change at least one of the division's operands to
float to get the proper result you desire.

(y*y)**(1./2)

Except that you should actually use the (built in) abs() function,

v = abs(y)

:-)

Next, the itervalues() method of dicts does not return a number, but
rather a "dictionary-valueiterator" object, as you can see from this:

|>> d = {}
|>> z = d.itervalues()
|>> z
<dictionary-valueiterator object at 0xb6f23c00>

In order to get values out of it you need to iterate over it like so:

for z in d.itervalues():
    # Do something with z's here...

You could also get both the keys and values at the same time by
iterating like so:

for k, z in d.iteritems():
    # Do something with k's and z's here...

(I'll come back to that.)

Now, a little further down in your function, you seem to want to use
the z value with the get() method of the dict to retrieve the key.
This won't work.

First, you're not *calling* the get method (that uses ()'s) you're
*subscripting* it (that uses []'s.)  If your code got this far, it
would break here.

|>> d = {}
|>> d.get
<built-in method get of dict object at 0xb7dd43e4>
|>> d.get[23]
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: unsubscriptable object

So you want to use ()'s, not []'s.

Further, get() works with *keys*, not *values*.  If you call it with a
value, it won't work, instead it will return None.  Unless, of course,
you *have* a key with the same value as your value, in which case it
will return the value associated with the key, but NOT the key
associated with the value (that you passed to get().)

In fact, there is no easy way to get the key from a dict given a value.
Dicts work the other way around.  (You basically have to iterate
through the values *and* keys, one pair at a time, testing each value
as you go.  This is very slow, compared to getting the value given a
key.)

value = d[key]

This is extremely fast.  It's pretty much the whole point of a
dictionary that this is very fast.

So, when you build the dict that you pass in to your function, you
might want to build it the other way round, i.e. make the keys the
values and the values keys.  However, you might not and here's why:

Dicts can only have one key of any given value of key.  Look:

|>> d = {1: 'a', 1: 'b'}
|>> d
{1: 'b'}
|>> d = {'a': 1, 'b': 1}
|>> d
{'a': 1, 'b': 1}

So, if you're running a mathematical function on a range of input (x's)
and storing the results (y's) as values in a dict, then you *do* want
the x's to be the keys and the y's to be the values.

I'm guessing that's what you're doing, and if so, you're doing it
correctly.

Let me address one last problem in your code above and then I'll show
you a neat way to return the key whose associated value is closest to
some value exvalue.


In this part of your code,

>         if v < 0.001:
>             u = dictionary.get[z]
>         return u

the return statement should be at the same indentation level as the
assignment statement:

if v < 0.001:
    u = dictionary.get[z]  # <- note, this won't work
    return u

or even just

if v < 0.001:
    return dictionary.get[z]  # <- note, this won't work

The way you have it now, the return statement would execute immediately
after the if statement, regardless of whether v < 0.001 or not.  Not
too bad if the very first value was close enough to exvalue. But if it
wasn't, 'u' would not have been defined by the time the return
statement tried to return it, and you would have gotten a NameError.
You probably already know this, and the indentation error was just a
minor typo.


So,  given a dict and an exvalue, how to return the key corresponding
to the value that's closest to exvalue?

here goes...


def pcloop(dictionary, exvalue):
    '''
    Return the key in dictionary whose value is
    closest to exvalue.

    If dictionary is empty, return None.
    '''

    # Get a iterator over *both* keys and values.
    diter = dictionary.iteritems()

    # Get the first (key, value) pair.
    try:
        u, z = diter.next()

    except StopIteration:
        # The dictionary was empty!
        # You might want to do something else here
        return

    # Compute the closeness of the first value.
    closest = abs(z - exvalue)

    # Create a var to store the closest key
    result = u

    # Iterate through the rest of the dict.
    for u, z in diter:

        # Compute the closeness.
        v = abs(z - exvalue)

        # Check if it's closer than the closest.
        if v < closest:

            # If so, store the new closest.
            closest = v

            # And store the new closest key.
            result = u

    return result


I hope that helps.  :-)

Python is an amazing language once you get the hang of it.  Enjoy.


Peace,
~Simon




More information about the Python-list mailing list