1

I have a function that contains code similar to the one below, which takes an OrdredDict object and a string as arguments:

def AddToOrderedDict(ordered_dict, new_key):
    ordered_dict[new_key] = []
    ordered_dict = OrderedDict(sorted(ordered_dict.items()))

This function will add the key to the dictionary and sort it, but not keep it sorted once it has left the function.

The code below demonstrates this behavior:

from collections import OrderedDict

def AddToOrderedDict(ordered_dict, new_key):
    ordered_dict[new_key] = ['New', 'List']
    ordered_dict = OrderedDict(sorted(ordered_dict.items()))
    print(dict(ordered_dict))

ordered_dict = OrderedDict()
ordered_dict['A'] = ['List', 'A']
ordered_dict['C'] = ['List', 'C']
ordered_dict['D'] = ['List', 'D']

AddToOrderedDict(ordered_dict, 'B')

print(dict(ordered_dict))

Output:

{'A': ['List', 'A'], 'B': ['New', 'List'], 'C': ['List', 'C'], 'D': ['List', 'D']}
{'A': ['List', 'A'], 'C': ['List', 'C'], 'D': ['List', 'D'], 'B': ['New', 'List']}

Why is the sorting not kept outside the function?

burritab
  • 25
  • 4
  • Changes to a local variable (by assigning another object to it) are lost when the containing function is left. The newly created OrderedDict never escapes the function. Only changes to an object itself (e. g. when handed as parameter into a function) are kept. – Michael Butscher Mar 11 '20 at 13:44
  • 2
    Your sorted dictionary never "leaves the function". You merely assigned it to a local variable, which vanishes at the end of the function. – jasonharper Mar 11 '20 at 13:45
  • 1
    When you do `ordered_dict = ...` you are creating a new `ordered_dict` variable in the local scope, not modifying the outer one. – solarc Mar 11 '20 at 13:45
  • Does this answer your question? [How do I pass a variable by reference?](https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference) – Harun Yilmaz Mar 11 '20 at 13:45
  • @jasonharper That answers my question perfectly, thank you! I did not know that it would assign as a local variable. – burritab Mar 11 '20 at 13:56
  • @solarc very good to know, thanks. Is there a way to act directly upon the passed variable, or will I need to return the local variable in order to get my desired output? – burritab Mar 11 '20 at 14:02

2 Answers2

1

Instead of modifying the variable for the local function scope, you should return the result to the global scope:

from collections import OrderedDict

ordered_dict = OrderedDict()

def AddToOrderedDict(ordered_dict, new_key):
    ordered_dict[new_key] = ['New', 'List']
    ordered_dict = OrderedDict(sorted(ordered_dict.items()))
    print(dict(ordered_dict))
    return ordered_dict


ordered_dict['A'] = ['List', 'A']
ordered_dict['C'] = ['List', 'C']
ordered_dict['D'] = ['List', 'D']

ordered_dict = AddToOrderedDict(ordered_dict, 'B')

print(dict(ordered_dict))

edit: changed the formulation

Haukland
  • 677
  • 8
  • 25
1

Variables in python are references to objects.

When you pass the parameter you have two variables pointing to the same object, one inside the function and one outside.

ordered_dict[new_key] = ["new","list"] modifies the existing OrderedDict object. So the change is visible outside the function.

ordered_dict = OrderedDict(sorted(ordered_dict.items())) on the other hand creates a new OrderedDict and changes the ordered_dict variablein the function to reffer to it, it has no affect on the ordered_dict variable in the main program.

If you can use external libraries you may want to look at the sortedcontainers module.


Is there a way to act directly upon the passed variable

Not a good one. There is a "move to end" function which could be used, but is likely to result in a bunch of unnessacery movement.

I suspect that the least bad way to sort an OrderedDict in-place is to empty and refill it, something like.

tmp = sorted(ordered_dict.items())
ordered_dict.clear()
ordered_dict.update(tmp)

Ultimatley though, doing a full resort on every insert is likely a bad design. A proper sorted container is likely to perform much better if it's available.

plugwash
  • 9,724
  • 2
  • 38
  • 51