Starting a dictionary for a loop with a specific key value

Here is the code:

EDIT **** Please no more "this is not possible with disordered vocabulary answers." I have known this for a long time. I made this post in case it MAY be possible, or someone has a workable idea.

#position equals some set of two dimensional coords for name in self.regions["regions"]: # I want to start the iteration with 'last_region' # I don't want to run these next two lines over every dictionary key each time since the likelihood is that the new # position is still within the last region that was matched. rect = (self.regions["regions"][name]["pos1"], self.regions["regions"][name]["pos2"]) if all(self.point_inside(rect, position)): # record the name of this region in variable- 'last_region' so I can start with it on the next search... # other code I want to run when I get a match return return # if code gets here, the points were not inside any of the named regions 

Hope the comments in the code explain my situation quite well. Suppose I was the last in the "delta" area (i.e., the key name is delta, the value will be the set of coordinates defining its boundaries), and I have another 500 regions. The first time I am in the region delta, the code may not have discovered this until it was supposedly (hypothetically) the 389th iteration ... so it did the 388 all(self.point_inside(rect, position)) calculations all(self.point_inside(rect, position)) before I discovered this. Since I will probably still be in deltas the next time I run (but I have to check that every time the code is executed), it would be useful if the "delta" key was the first one that was checked by the for loop.

This particular code can run many times per second for many different users .. therefore speed is critical. The design is such that very often the user will not be in the region, and all 500 entries may need to be scrolled and exit the loop without coincidence, but I would like to speed up the overall program, speeding it up for those that are currently in one from the regions.

I don't need the extra overhead of sorting the dictionary in any particular order, etc. I just want him to start looking for the last one that he successfully matched all(self.point_inside(rect, position))

Maybe this will help a little more. Below is the dictionary I'm using (only the first record shown) so you can see the structure I encoded above ... and yes, despite the name "rect" in the code, it actually checks for a point in the cubic area.

{"regions": {"shop": {"flgs": {"breakprot": true, "placeprot": true}, "dim": 0, "placeplayers": {"4f953255-6775-4dc6- a612-fb4230588eff ":" SurestTexas00 "}," breakplayers ": {" 4f953255-6775-4dc6-a612-fb4230588eff ":" SurestTexas00 "}," protected ": true," banplayers ": {}," pos1 ": [5120025, 60 , 5120208], "pos2": [5120062, 73, 5120257], "ownerUuid": "4f953255-6775-4dc6-a612-fb4230588eff", "accessplayers": {"4f953255-6775-4dc6-a612-fb4230588eff": " SurestTexas00 "}}, more, more, more ...}

+5
source share
6 answers

You can try to implement some caching mechanism in a custom dict subclass.

You can set self._cache = None to __init__ , add a method like set_cache(self, key) to set the cache, and finally override __iter__ to yield self._cache before calling __iter__ by default.

However, this can be rather cumbersome if you consider fooobar.com/questions/42089 / ... as well as this one .

For what he wrote in your question, I would try instead to implement this caching logic in your code.

 def _match_region(self, name, position): rect = (self.regions["regions"][name]["pos1"], self.regions["regions"][name]["pos2"]) return all(self.point_inside(rect, position)) if self.last_region and self._match_region(self.last_region, position): self.code_to_run_when_match(position) return for name in self.regions["regions"]: if self._match_region(name, position): self.last_region = name self.code_to_run_when_match(position) return return # if code gets here, the points were not inside any of the named regions 
+3
source

Instead of a for loop, you can use iterators directly. Here is an example function that does something similar to what you want using iterators:

 def iterate(what, iterator): iterator = iterator or what.iteritems() try: while True: k,v = iterator.next() print "Trying k = ", k if v > 100: return iterator except StopIteration: return None 

Instead of storing the area name in last_region , you save the result of this function, which is like a β€œpointer” to where you left off. Then you can use this function as shown in the figure (shown as if it were running in the interactive Python interpreter, including output):

 >>> x = {'a':12, 'b': 42, 'c':182, 'd': 9, 'e':12} >>> last_region = None >>> last_region = iterate(x, last_region) Trying k = a Trying k = c >>> last_region = iterate(x, last_region) Trying k = b Trying k = e Trying k = d 

That way, you can easily return from where you left off, but there is another caveat:

 >>> last_region = iterate(x, last_region) Trying k = a Trying k = c >>> x['z'] = 45 >>> last_region = iterate(x, last_region) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in iterate RuntimeError: dictionary changed size during iteration 

As you can see, this will throw an error if you ever add a new key. Thus, if you use this method, you must definitely set last_region = None when adding a new area to the dictionary.

+2
source

That's right, the dictionary is disordered. Therefore, OrderedDict will not help you with what you want to do.

You can save the last region in your class. Then, on the next call, check if the last region is saved before checking the entire dictionary?

+1
source

TigerhawkT3 is right. Dictations are disordered in the sense that there is no guaranteed order or keys in this dictionary. You may even have a different order of keys if you iterate over the same dictionary. If you need an order, you need to use OrderedDict or just a regular list. You can convert your dict to a list and sort it as it represents the desired order.

0
source

Not knowing what you have, and self in the example is a user instance or an environment instance, it's hard to find a solution. But if self in this example is an environment, its class may have a class attribute, which is a dictionary of all current users and their last known position, if the user instance hashable.

Something like that

 class Thing(object): __user_regions = {} def where_ami(self, user): try: region = self.__user_regions[user] print 'AHA!! I know where you are!!' except KeyError: # find region print 'Hmmmm. let me think about that' region = 'foo' self.__user_regions[user] = region class User(object): def __init__(self, position): self.pos = position thing = Thing() thing2 = Thing() u = User((1,2)) v = User((3,4)) 

Now you can try to extract the user's region from the class attribute. If more than one Thing exists, they will share this class attribute.

 >>> >>> thing._Thing__user_regions {} >>> thing2._Thing__user_regions {} >>> >>> thing.where_ami(u) Hmmmm. let me think about that >>> >>> thing._Thing__user_regions {<__main__.User object at 0x0433E2B0>: 'foo'} >>> thing2._Thing__user_regions {<__main__.User object at 0x0433E2B0>: 'foo'} >>> >>> thing2.where_ami(v) Hmmmm. let me think about that >>> >>> thing._Thing__user_regions {<__main__.User object at 0x0433EA90>: 'foo', <__main__.User object at 0x0433E2B0>: 'foo'} >>> thing2._Thing__user_regions {<__main__.User object at 0x0433EA90>: 'foo', <__main__.User object at 0x0433E2B0>: 'foo'} >>> >>> thing.where_ami(u) AHA!! I know where you are!! >>> 
0
source

You say that you "do not need additional overhead to sort the dictionary in any particular order." What is the overhead? Presumably, OrderedDict uses some additional data structure internally to keep track of the order of the keys. But if you don't know that it takes too much memory, then OrderedDict is your solution. This means that you need to profile your code and make sure that OrderedDict is the source of your bottleneck.

If you need the cleanest code, just use OrderedDict . It has a move_to_back method that takes a key and places it either at the beginning of the dictionary or at the end. For instance:

 from collections import OrderedDict animals = OrderedDict([('cat', 1), ('dog', 2), ('turtle', 3), ('lizard', 4)]) def check_if_turtle(animals): for animal in animals: print('Checking %s...' % animal) if animal == 'turtle': animals.move_to_end('turtle', last=False) return True else: return False 

Our check_if_turtle function scans the OrderedDict for the turtle key. If it does not find it, it returns False . If he finds it, it will return True , but not after moving the turtle key to the beginning of the OrderedDict .

Give it a try. On first start:

 >>> check_if_turtle(animals) Checking cat... Checking dog... Checking turtle... True 

we see that he checked all the keys before turtle . Now, if we run it again:

 >>> check_if_turtle(animals) Checking turtle... True 

we see that he first checked the turtle key.

0
source

All Articles