Would like example of array sorting

Issue #184 resolved
Mitchell Model
created an issue

[I couldn't figure out how to ask for this other than creating an issue.]

I would like to see an example of NSMutableArray sorting, using sortedArrayUsingDescriptors/NSSortDescriptor and/or sortedArrayUsingComparator. (What I really want to know is how to pass a selector and/or block into an Objective-C method call from Python, which would have applications beyond sorting.)

Comments (8)

  1. Ronald Oussoren repo owner

    Block support in PyObjC is documented here: https://pythonhosted.org/pyobjc/core/blocks.html

    Basically pass a python callable as the block argument, PyObjC takes care of the conversion to an Objective-C block.

    Selectors are basically method names and can be passed to Objective-C a byte-strings, that is the following Objective-C code:

    [obj setDelegateSelector:@selector(foo:)]
    

    Is written like this in Python:

    obj.setDelegateSelector_(b"foo:")
    
  2. Mitchell Model reporter

    Thanks. That helps some. Here's the code I am struggling with (Python 3). I want to sort the list of dictionaries in Objective-C rather than in Python (for obscure reasons, but if I can get this code working I assume I can get anything else using keys and/or selectors working too).

    def sort(lst, key):
        return lst.sortedArrayUsingDescriptors_(
            [NSSortDescriptor.sortDescriptorWithKey_ascending_selector_(
                key, True, b'caseInsensitiveCompare_')])
    
    lst = [{'k': 1, 'x': 2, },
           {'k': 0, 'x': 3, },
           {'k': 3, 'x': 5, },
           {'k': 2, 'x': 0, },
           ]
    
    print(sort(let, 'k'))
    

    I am confused about what needs to be a bytearray and what a regular string, where underscores get used and where colons, and by whether I have to convert my list of dictionaries to an NSArray of NSDictionary. I think I have tried all possible combinations (except the one that would work that :-)) and can't get this working.

    Also need to get this working for 2.7.

    Great stuff, great fun.

  3. Terrence Katzenbaer

    @Mitchell Model It doesn't look like the code you provided in your last post will run since [NSSortDescriptor.sortDescriptor.... is not valid Python. Additionally, if you look at Ronald's post, the selectors should be in their "Objective-C form", i.e. with the colons instead of underscores.

  4. Mitchell Model reporter

    "[NSSortDescriptor.sortDescriptor.... is not valid Python" - why not? Doesn't this read "list of NSSortDescriptor"?

    This is the Objective-C code I am trying to convert:

    NSArray* sorted(NSArray* lst, NSString* key){
        return [lst sortedArrayUsingDescriptors:
                @[[NSSortDescriptor
                   sortDescriptorWithKey:key
                   ascending:YES
                   selector: @selector(caseInsensitiveCompare:)]]];
    }
    

    Here is a more complete example of my attempt to get this to work in pyobjc, with the descriptor key ending with a colon (as I had tried before my previous post).

    def sort(lst, key):
        return lst.sortedArrayUsingDescriptors_(
            [NSSortDescriptor.sortDescriptorWithKey_ascending_selector_(
                key, True, 'caseInsensitiveCompare:')])
    
    lst = [{b'k': 1, 'x': 2, },
           {b'k': 0, 'x': 3, },
           {b'k': 3, 'x': 5, },
           {b'k': 2, 'x': 0, },
           ]
    
    
    print sort(NSMutableArray.arrayWithArray_(lst),
               NSString.stringWithString_('k'))
    

    I get an error:

    ValueError: NSInvalidArgumentException - -[OC_PythonNumber caseInsensitiveCompare:]: unrecognized selector sent to instance 0x7fa8c2464cf0
    

    Is that error message indicating that an attempt was made to send a number the message caseInsensitiveCompare:? I don't see why that would be happening.

  5. Ronald Oussoren repo owner

    The translation looks OK, except for the use of "caseInsensitiveCompare:". That's a selector for NSString, and not of NSNumber. The descriptor you created tries to sort the list based on a case-insensitive compare of the values for dict key "k". Those values are integers and integers don't have a method for doing case-insensitive compares. Use "compare:" instead as the name of the selector for performing the comparison, or use strings for the values for dict key "k".

  6. Mitchell Model reporter

    Yes that was pretty stupid of me Thanks. I had gotten so unnerved about shuffling around possible formulations that I didn't notice that I had oversimplified the example. Off and running -- thanks.

  7. Log in to comment