Best way to sort a list?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Message
Author
Human Bolt Diary
Regular
Posts: 111
Joined: Fri Oct 11, 2013 12:46 am
Contact:

Best way to sort a list?

#1 Post by Human Bolt Diary »

Hi everybody,

Let's say I want a button that sorts a list. Would the best way to approach that be something like extending the Action class with python's sorted function:

Code: Select all


class SortList(Action):
 def __init__(self, list, *args):
  self.my_list = list

 def getKey(self,item):
  return item

 def__call__(self):
  sorted_list = sorted(self.my_list, key=self.getKey)
  self.my_list = sorted_list

Or does Ren'Py have a function built-in to sort lists and lists of lists?

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Best way to sort a list?

#2 Post by xela »

Human Bolt Diary wrote:Hi everybody,

Let's say I want a button that sorts a list. Would the best way to approach that be something like extending the Action class with python's sorted function:

Code: Select all


class SortList(Action):
 def __init__(self, list, *args):
  self.my_list = list

 def getKey(self,item):
  return item

 def__call__(self):
  sorted_list = sorted(self.my_list, key=self.getKey)
  self.my_list = sorted_list

Or does Ren'Py have a function built-in to sort lists and lists of lists?
Ren'Py doesn't, at least I know of none. I've didn't get a chance to mess with Action yet (but really want to). You code doesn't look like it'll work, unless someone posts a proper reply, I'll see if I can do the messing around ina dummy project.
Like what we're doing? Support us at:
Image

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Best way to sort a list?

#3 Post by xela »

Code: Select all

init python:
    from random import shuffle
    from copy import copy
    
    class SortContainer(Action):
        """
        Sorts an iterable container on key parameter.
        """
        def __init__(self, container, key=None):
            self.container = container
            self.key = key
        
        def __call__(self):
            for key in renpy.store.__dict__:
                if renpy.store.__dict__[key] is self.container:
                    renpy.store.__dict__[key] = sorted(self.container, self.key)
            return 1

label start:
    $ l = range(100)
    $ shuffle(l)
    $ z = copy(l)
    show screen test_sorting

    while True:
        $result = ui.interact()
        
screen test_sorting:
    hbox:
        xalign 0.5
        xmaximum config.screen_width
        box_wrap True
        for i in l:
            text "[i]"
            
    hbox:
        xalign 0.5
        yalign 0.4
        xmaximum config.screen_width
        box_wrap True
        for i in z:
            text "[i]"
            
    textbutton "Sort":
        align (0.5, 0.9)
        action SortContainer(l)
This works and it doesn't sort z due to "is" instead of "==" but this is still an awkward code, I couldn't figure out how to do it differently with Action inheritance...
Like what we're doing? Support us at:
Image

Anima
Veteran
Posts: 448
Joined: Wed Nov 18, 2009 11:17 am
Completed: Loren
Projects: PS2
Location: Germany
Contact:

Re: Best way to sort a list?

#4 Post by Anima »

You can use the [].sort() method that will sort the list in place.
Avatar created with this deviation by Crysa
Currently working on:
  • Winterwolves "Planet Stronghold 2" - RPG Framework: Phase III

Human Bolt Diary
Regular
Posts: 111
Joined: Fri Oct 11, 2013 12:46 am
Contact:

Re: Best way to sort a list?

#5 Post by Human Bolt Diary »

xela wrote:

Code: Select all

init python:
    from random import shuffle
    from copy import copy
    
    class SortContainer(Action):
        """
        Sorts an iterable container on key parameter.
        """
        def __init__(self, container, key=None):
            self.container = container
            self.key = key
        
        def __call__(self):
            for key in renpy.store.__dict__:
                if renpy.store.__dict__[key] is self.container:
                    renpy.store.__dict__[key] = sorted(self.container, self.key)
            return 1

label start:
    $ l = range(100)
    $ shuffle(l)
    $ z = copy(l)
    show screen test_sorting

    while True:
        $result = ui.interact()
        
screen test_sorting:
    hbox:
        xalign 0.5
        xmaximum config.screen_width
        box_wrap True
        for i in l:
            text "[i]"
            
    hbox:
        xalign 0.5
        yalign 0.4
        xmaximum config.screen_width
        box_wrap True
        for i in z:
            text "[i]"
            
    textbutton "Sort":
        align (0.5, 0.9)
        action SortContainer(l)
This works and it doesn't sort z due to "is" instead of "==" but this is still an awkward code, I couldn't figure out how to do it differently with Action inheritance...
That's really interesting, I wouldn't have thought of using renpy.store.__dict__

My biggest issue was getting the sorted list assigned to the right variable.

Off the top of my head, a small tweak would be an option for reverse sorting, like...

Code: Select all


    class SortContainer(Action):
        """
        Sorts an iterable container on key parameter.
        """
        def __init__(self, container, key=None,reverse=False):
            self.container = container
            self.key = key
            self.reverse = reverse
        
        def __call__(self):
            for key in renpy.store.__dict__:
                if renpy.store.__dict__[key] is self.container:
                    renpy.store.__dict__[key] = sorted(self.container, reverse=self.reverse, key=self.key)
                
            return 1

Which seems to work.

Human Bolt Diary
Regular
Posts: 111
Joined: Fri Oct 11, 2013 12:46 am
Contact:

Re: Best way to sort a list?

#6 Post by Human Bolt Diary »

Here's a modified example that removes the need for "while True:", but I'm afraid I don't know quite enough about the consequences of "renpy.restart_interaction()" to say if this is correct.

Code: Select all

init python:
    from random import shuffle
    from copy import copy
    
    class SortContainer(Action):
        """
        Sorts an iterable container on key parameter.
        """
        def __init__(self, container, key=None,reverse=False):
            self.container = container
            self.key = key
            self.reverse = reverse
     
        def __call__(self):
            for key in renpy.store.__dict__:
                if renpy.store.__dict__[key] is self.container:
                    renpy.store.__dict__[key] = sorted(self.container, reverse=self.reverse, key=self.key)

            renpy.restart_interaction()
label start:
    $ l = range(100)
    $ shuffle(l)
    $ z = copy(l)

    call screen test_sorting
    
screen test_sorting:
    hbox:
        xalign 0.5
        xmaximum config.screen_width
        box_wrap True
        for i in l:
            text "[i]"
            
    hbox:
        xalign 0.5
        yalign 0.4
        xmaximum config.screen_width
        box_wrap True
        for i in z:
            text "[i]"
            
    textbutton "Sort":
        align (0.5, 0.9)
        action [ SortContainer(l) ]

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Best way to sort a list?

#7 Post by xela »

Human Bolt Diary wrote:Here's a modified example that removes the need for "while True:", but I'm afraid I don't know quite enough about the consequences of "renpy.restart_interaction()" to say if this is correct.
It's not correct and while loop is not required for this class to work in any way or form. It was there to prevent label from running out and return to main menu so I could watch the results of sorting in peace. Assuming that you were planning using this in game's context, you'll be ok without the loop.

PS: With loop in the game, it would have been simpler to return the list and have it sorted inside it without the need to access store's dictionary.
Like what we're doing? Support us at:
Image

Human Bolt Diary
Regular
Posts: 111
Joined: Fri Oct 11, 2013 12:46 am
Contact:

Re: Best way to sort a list?

#8 Post by Human Bolt Diary »

xela wrote:
Human Bolt Diary wrote:Here's a modified example that removes the need for "while True:", but I'm afraid I don't know quite enough about the consequences of "renpy.restart_interaction()" to say if this is correct.
It's not correct and while loop is not required for this class to work in any way or form. It was there to prevent label from running out and return to main menu so I could watch the results of sorting in peace. Assuming that you were planning using this in game's context, you'll be ok without the loop.

PS: With loop in the game, it would have been simpler to return the list and have it sorted inside it without the need to access store's dictionary.
I see now. I was confused about the reason for the loop.

If I'm understanding correctly, your class sorts the list and changes the variable in the store's dictionary to the sorted list. This is independent from the while loop and ui.interact(), which is keeping the screen alive to refresh the numbers. Without the loop, the variable is stored and the player is returned to where they left off in the script.

Thanks for the help.

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Best way to sort a list?

#9 Post by xela »

Human Bolt Diary wrote: If I'm understanding correctly, your class sorts the list and changes the variable in the store's dictionary to the sorted list. This is independent from the while loop and ui.interact(), which is keeping the screen alive to refresh the numbers. Without the loop, the variable is stored and the player is returned to where they left off in the script.

Thanks for the help.
Yeap, that's all right except the loop kept the entire game alive, screen would have died with the game (screen that is shown, not called has to be hidden, called screen will disappear on the first successful interaction). Show screen doesn't really take you out of game context so you can't really say that you would be returned to where ever you came from, screen can be active and you can also go on with your game. That's besides the point, that sorting class got nothing to go with it, it's just how Ren'Py is setup to work.
Like what we're doing? Support us at:
Image

Anima
Veteran
Posts: 448
Joined: Wed Nov 18, 2009 11:17 am
Completed: Loren
Projects: PS2
Location: Germany
Contact:

Re: Best way to sort a list?

#10 Post by Anima »

That approach has a drawback though.
If the list is referenced by another name, that name will still point to the old list. Of course that will only cause trouble if there is more than one reference to the list, but it's something to keep in mind.
Avatar created with this deviation by Crysa
Currently working on:
  • Winterwolves "Planet Stronghold 2" - RPG Framework: Phase III

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Best way to sort a list?

#11 Post by xela »

Anima wrote:That approach has a drawback though.
If the list is referenced by another name, that name will still point to the old list. Of course that will only cause trouble if there is more than one reference to the list, but it's something to keep in mind.
That's not true. We iterate over the whole global namespace, searching for any variable (label) that points to but does not necessarily equals to the list (or label to a list if you want to get technical since Python just passes "labels" around). If we wrote z = l in my example instead of making a shallow copy, both variables would point to the new list and old list would have been killed by the garbage collector unless referenced to from outside of global scope or Ren'Py not do something crazy for rollback support.

That said, iterating over the whole global namespace just to sort a list is not the best way to sort it :) I just used this as an exercise to start using custom screen actions in my game (and already am).
Like what we're doing? Support us at:
Image

Anima
Veteran
Posts: 448
Joined: Wed Nov 18, 2009 11:17 am
Completed: Loren
Projects: PS2
Location: Germany
Contact:

Re: Best way to sort a list?

#12 Post by Anima »

True, it doesn't refer to the old list, but it would still not refer to the same list since you assign a new list to it. So if a,b refer to the same list object and you use sort on a once, a and b will not point to the same list any more. If you'd use sort on a again b wouldn't be affected.
Avatar created with this deviation by Crysa
Currently working on:
  • Winterwolves "Planet Stronghold 2" - RPG Framework: Phase III

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Best way to sort a list?

#13 Post by xela »

Anima wrote:True, it doesn't refer to the old list, but it would still not refer to the same list since you assign a new list to it. So if a,b refer to the same list object and you use sort on a once, a and b will not point to the same list any more. If you'd use sort on a again b wouldn't be affected.
Yeap... so we get a different behavior from if we simply used sorted (would sort one list but leave the other one along). We can't break out of the for loop either since there is no way of knowing witch list to sort...
Like what we're doing? Support us at:
Image

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Best way to sort a list?

#14 Post by xela »

Code: Select all

    class SortContainer(Action):
        """
        Sorts an iterable container on key parameter.
        Expects a string representation of a variable assigned to a container as first argument.
        """
        def __init__(self, container):
            self.container = container 
        def __call__(self):
            renpy.store.__dict__[self.container] = sorted(renpy.store.__dict__[self.container])
            return "__"
SetVariable() approach: where they pass a string representation of a variable as argument. Add args and kwargs and this is fairly close to what I'd be willing to use in my game :)

Great job finding holes to poke with the last approach btw.
Like what we're doing? Support us at:
Image

Anima
Veteran
Posts: 448
Joined: Wed Nov 18, 2009 11:17 am
Completed: Loren
Projects: PS2
Location: Germany
Contact:

Re: Best way to sort a list?

#15 Post by Anima »

How about this?

Code: Select all

    class SortList(Action):
        def __init__(self, pList):
            self.list = pList
        def __call__(self):
            self.list.sort()
            return True
It sorts the list in place, so it doesn't change any references. As a plus point it also works for lists that aren't directly referenced from the store. Like lists that are referenced by an objects field.
Avatar created with this deviation by Crysa
Currently working on:
  • Winterwolves "Planet Stronghold 2" - RPG Framework: Phase III

Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot]