0

A dictionary like Dict = {'Key_1': 'Data_1', 'Key_2': 'Data_2', ..., 'Key_n': 'Data_n'} contains n keys named in ascending order.

A method like AddNextKey(Dict, NewData) should determine the last key n and add a new one n+1 with value NewData. If Dict is empty, the method should append key Key_1.

I am quite sure that a pythonic solution does not require more than two or three lines of code to achieve this.

Remark: It is not important that the dictionary is ordered. Only the correct next key (n+1) must be appended.

Could anybody give advice?

Rickson
  • 1,040
  • 2
  • 16
  • 40
  • 9
    If your keys ascend numerically, then why not use a list, and append to the end of the list? – turbulencetoo Jan 12 '17 at 22:16
  • 1
    What's your current solution, and what's unpythonic about it? – jonrsharpe Jan 12 '17 at 22:18
  • What? As per my understanding `dict`s are un-ordered. How are they ascending? Please can you explain a bit? Do you mean keys are in continuation? – Moinuddin Quadri Jan 12 '17 at 22:19
  • If you're looking at having a sorted dictionary, I would use orderedDict from the [collections module](https://docs.python.org/2/library/collections.html) – Fruitspunchsamurai Jan 12 '17 at 22:22
  • Wow. So many comments in such a short time :) @turbulencetoo: Unfortunately, I need to have a dictionary due to some other external constraints. – Rickson Jan 12 '17 at 22:24
  • @jonrsharpe: Well, it is using way to many loops... – Rickson Jan 12 '17 at 22:24
  • @Moinuddin Quadri: Yes. Key_1, Key_2, Key_3, Key_4, ..., Key_10, ..., Key_100, ..., Key_n – Rickson Jan 12 '17 at 22:24
  • 1
    Please add it to the question. If you have **working code** that you think could be improved, it may be a better fit for [codereview.se]. – jonrsharpe Jan 12 '17 at 22:24
  • you can always keep `n` as element in dictionary - ie. `Dict['last_n'] = 100` and use in your function. – furas Jan 12 '17 at 22:29
  • @jonrsharpe I don't have access to my lab PC right now but it looks pretty much like the one posted by Michael Ellner – Rickson Jan 12 '17 at 22:33
  • I wonder if it even could be considered pythonic to do this? It's complicated to get it right, and what's the benefit? – Roland Smith Jan 12 '17 at 22:53

7 Answers7

2

I'm not sure if it's the most Pythonic solution but my approach is to subclass dict and add that additional method. Since it's a subclass, you can use an instance of NextKeyDict anywhere you could an actual dict, but also perform these special operations where needed.

class NextKeyDict(dict):
    def add_next_key(self, new_data):
        if not self.keys():
            self[0] = new_data
            return

        last_key = sorted(self.keys())[-1]
        new_key = last_key + 1
        self[new_key] = new_data

d = NextKeyDict()
d.add_next_key('foo')
d.add_next_key('bar')
d.add_next_key('baz')
print(d)

Output

{0: 'foo', 1: 'bar', 2: 'baz'}
Tagc
  • 8,736
  • 7
  • 61
  • 114
  • @RolandSmith Right. Python is a language of "consenting adults", right? My thinking would be to ensure this class/method has appropriate documentation that explains it's only suitable for use with integer keys and leave the burden of ensuring that on the user. What modifications would you make? – Tagc Jan 12 '17 at 23:06
  • I would override some methods to ensure that you cannot insert an improper key. Not to be pedantic, but to make the solution robust. To catch mistakes before they turn into bugs. The more special rules ("all keys in dict X should have the format 'Key_N'") code has, the more the programmers need to keep in their mind. – Roland Smith Jan 12 '17 at 23:14
  • @RolandSmith I think that would break the Liskov Substitution Principle. Right now my dict can be used "as is" by any class that expects a `dict`, and the only potential pitfall would be to call `add_next_key` when the keys aren't integers - but classes expecting `dict` would not be aware of this method anyway. You make a valid point though - an alternative would be to use *composition* instead of subclassing, to make a class that internally uses a dict but throws exceptions if trying to store non-integers as keys. – Tagc Jan 12 '17 at 23:20
  • Remember the mistake that was [Java's Properties class](http://stackoverflow.com/questions/873510/why-does-java-util-properties-implement-mapobject-object-and-not-mapstring-st) haha? Yeah, a composition approach may have been better here. – Tagc Jan 12 '17 at 23:21
  • Luckily, I've never been unfortunate enough to have to use Java. – Roland Smith Jan 12 '17 at 23:23
1

You could use the length of the dictionary

Dict = dict()
for i in range(10):
    Dict['key_' + str(len(Dict)+1)] = 'value_'+str(len(Dict)+1)
print Dict

Outputs

{'key_10': 'value_10', 'key_5': 'value_5', 'key_4': 'value_4', 'key_7': 'value_7', 'key_6': 'value_6', 'key_1': 'value_1', 'key_3': 'value_3', 'key_2': 'value_2', 'key_9': 'value_9', 'key_8': 'value_8'}
1

There is no order in a dictionary. And you can not get the last inserted element from it.

Take a look at this example:

>>> {'Key_1': 'Data_1', 'Key_2': 'Data_2'} == {'Key_2': 'Data_2', 'Key_1': 'Data_1'}
True

As you can see, the order is meaningless for the dict.

The question is what exactly do you mean by the last key n. Last inserted? Or with biggest key_%index%? In first case you better to use OrderedDict, as already mentioned.

However, here is my solution, if I got you right.

def AddNextKey(Dict, NewData):
    Dict['Key_%d' % (len(Dict) + 1)] = NewData

d = {}
AddNextKey(d, 'Data_1')
AddNextKey(d, 'Data_2')
AddNextKey(d, 'Data_3')

print d

You will get

{'Key_1': 'Data_1', 'Key_3': 'Data_3', 'Key_2': 'Data_2'}
vasi1y
  • 765
  • 5
  • 13
1

Normal Python dictionaries are unordered. So technically, there is no "last" key.

But there is a way around that. You could use the len method to see how many items there are in the dictionary, and construct the next key based on that.

But note that this method is fragile. if you accidentally add a key 'foo' to the dictionary, your whole scheme is off.

To really fix this you would have to create a subclass of dict (by overriding the __setitem__ and update methods as shown in this answer) so that it only accepts valid keys in the form 'Key_N'. You could then also add an append method to automatically create the next key.

But as turbulencetoo commented, it would probably be easier to use a list.

Community
  • 1
  • 1
Roland Smith
  • 42,427
  • 3
  • 64
  • 94
0
# set-up the dictionary for demo purposes
testdict = {x:y for x,y in zip(["Key_"+str(x) for x in range(1,7)],
                               ["Data_"+str(x) for x in range(1,7)])}

# fetch most recent key
# add 1 to recent_key_val and insert new value
if len(testdict) != 0:
    recent_key_val = max([int(x.split("_")[1]) for x in testdict])
    testdict["Key_"+str(recent_key_val +1)] = "New_Data"
else:
    testdict["Key_1"] = "New_Data"
Kyrubas
  • 877
  • 8
  • 23
0

Something you need is already built into Python. So I think the most pythonic way is to use OrderedDict. As the docs say

Return an instance of a dict subclass, supporting the usual dict methods. An OrderedDict is a dict that remembers the order that keys were first inserted. If a new entry overwrites an existing entry, the original insertion position is left unchanged. Deleting an entry and reinserting it will move it to the end.

from collections import OrderedDict
d = OrderedDict()
d["Key1"] = 1
d["Key2"] = 2
d["Key3"] = 3

last_key = d.keys()[-1]  # "Key3" thanks to ordering
new_key = calculate_new_key(last_key)
d[new_key] = new_value
yogabonito
  • 657
  • 5
  • 14
0

By definition, python dictionary are an unordered set of key: value pairs, with the requirement that the keys are unique. Given that you can use OrderedDict from class collections to create and update the dictionary and preserve the order.

from collections import OrderedDict
d1 = OrderedDict([(1, 'Data_1'), (2, 'Data_2'),(3, 'Data_3')])

You get

OrderedDict([(1, 'Data_1'), (2, 'Data_2'), (3, 'Data_3')])

Now the method to update the dictionary

def AddNextKey(Dict, NewData):
    last_key = next(reversed(Dict))
    new_key = last_key + 1
    Dict.update({new_key: NewData})

When you call

AddNextKey(d1, 'Blah')

You get

OrderedDict([(1, 'Data_1'), (2, 'Data_2'), (3, 'Data_3'), (4, 'Blah')])
Vaishali
  • 37,545
  • 5
  • 58
  • 86