1

I'd like to run a function on all the values in a dictionary. To make things more complicated, the exact transformation each value goes through is not independent, but contingent on the transformations other values before it went through. Here's a simple function, defined on a list, to show what I mean:

def new_list_generator(mylist):
    number_of_iterations = 0
    new_list  = []
    for n in mylist:
        if number_of_iterations%2 == 0:
            new_list.append(n**1.25)
            number_of_iterations += 1
        else:
            new_list.append(n**0.5)
            number_of_iterations += 1
    return new_list

You can see that the power we employ on each value depends on the the history - how many values were modified before it.

Now, that's the function defined for a list. What happens when I want to modify a dictionary's values without disassociating keys and values (that is, I don't want to simply create a list of all the values and feed that list to the function)?

The best solution I could think of (and make it work) was the following cumbersome solution:

step 1: transform the dictionary to a list of tuples. i.e. [(value1,key1), (value2,key2),...]

step 2: run a modified function on that list.

step 3: convert the list back to a dictionary with dict().

That is:

some_dict = {"Graham": 13, "Eric": 19, "Terry G": 7, "Terry J":11, "John": 15, "Michael": 7}

dict_to_tuples = some_dict.items()

def new_list_generator1(mylist):      # notice it's a new function
    number_of_iterations = 0
    new_list  = []
    for n in mylist:
        if number_of_iterations%2 == 0:
            new_list.append((n[0],n[1]**1.25))   # <== new
            number_of_iterations += 1
        else:
            new_list.append((n[0],n[1]**0.5))    # <== new
            number_of_iterations += 1
    return new_list

tups = new_list_generator1(dict_to_tuples)

print dict(tups)

I was wondering if there's a less cumbersome way to do that, hopefully without having to modify the original list. I googled around and couldn't find anything informative on Stackoverflow or elsewhere (where the function employed on dict values depends on history).

Thanks for your help.

Optimesh
  • 2,667
  • 6
  • 22
  • 22
  • 1
    You do know dicts have almost no iteration order guarantees, right? Why do you want to process the entries of your dict in a way dependent on the iteration order? – user2357112 Jul 07 '14 at 09:00
  • If you _do_ care about iteration order, you may want to have a look at [this answer](http://stackoverflow.com/questions/60848/how-do-you-retrieve-items-from-a-dictionary-in-the-order-that-theyre-inserted/61031#61031). – merlin2011 Jul 07 '14 at 09:03
  • @user2357112 yes, I know, thank you. Good point. This is actually just a function I used for the sake of example. The function I actually need to use in my code is more complicated (and doesn't mind the order), so I figured I'll save everyone the headache :) – Optimesh Jul 07 '14 at 09:03

2 Answers2

1

Just loop through the keys and modify the values directly.

some_dict = {"Graham": 13, "Eric": 19, "Terry G": 7, "Terry J":11, "John": 15, "Michael": 7}


def new_list_generator1(mydict):      # notice it's a new function
    number_of_iterations = 0
    for key in mydict:
        if number_of_iterations%2 == 0:
            mydict[key] = mydict[key]**1.25
        else:
            mydict[key] = mydict[key]**0.5
        number_of_iterations += 1
    return mydict

print new_list_generator1(some_dict)

As @kroolik correctly pointed out below, this can be simplified further with enumerate at least for the current example.

def new_list_generator1(mydict):      # notice it's a new function
    for i, key in enumerate(mydict):
        if i%2 == 0: mydict[key] = mydict[key]**1.25
        else: mydict[key] = mydict[key]**0.5
    return mydict
merlin2011
  • 71,677
  • 44
  • 195
  • 329
0

As pointed out in the comments, it's quite strange to process values of a dict in terms of the iteration order, because that order is not guaranteed, so it could lead to different values every time you apply the function. That being said, if the only parameter that you take into account when processing is the iteration step, you could simply use a dict comprehension:

{key: some_dict[key]**([1.25, 0.5][i % 2]) for i, key in enumerate(some_dict)}

Or more generally

def process_value(value, iteration_step):
    # new_value = ...
    return new_value

{key: process_value(some_dict[key], i) for i, key in enumerate(some_dict)}
Seb D.
  • 5,046
  • 1
  • 28
  • 36