开发者

Slice NSArray from end of array

What is the best way to "slice" an NSArray from the en开发者_StackOverflow社区d, rather than the beginning, of the array (for example, finding the subarray containing the last few elements of a NSArray of unknown length)? In Python, you can use negative indices to accomplish this, e.g.:

new_list = old_list[-5:-3]

What's the most natural way to do this in Objective-C?


There's nothing to match Python's nice syntax for this, but you could do:

NSUInteger count = [myArray count];
NSArray * slice = [myArray subarrayWithRange:(NSRange){count-n, n}];

You could also write up a category for NSArray, something like:

@interface NSArray (jrdioko_slice)
- (NSArray *) jrdioko_sliceFrom:(NSInteger)start to:(NSInteger)stop;
@end

If you want to go this route, the Python source will certainly repay study. A list object creates a slice object when a slice operation is performed. The relevant method on a slice object is PySlice_GetIndicesEx. You'll just have to be careful turning those indexes into an NSRange. As the comment in that function warns "this is harder to get right than you might think". (I'll try to take a crack at this later.)

UPDATE: Here we have a slice category on NSArray. The index calculation logic is pretty much straight out of the Python code that I linked to above.* It's actually a lot easier than I thought at first if you don't have to worry about the stride part of a Python slice. I've run this through a few tests and it seems to work the same as the Python version.

@interface NSArray (WSS_Slice)
- (NSArray *)WSS_arrayBySlicingFrom:(NSInteger)start to:(NSInteger)stop;
@end

// Python allows skipping any of the indexes of a slice and supplies default
// values. Skipping an argument to a method is not possible, so (ab)use 
// NSNotFound as "not specified" index value. The other way to do this would
// be with varargs, which might be even handier if one decided to implement
// the stride functionality.
enum {
    WSS_SliceNoIndex = NSNotFound
};

@implementation NSArray (WSS_Slice)

- (NSArray *)WSS_arrayBySlicingFrom:(NSInteger)start to:(NSInteger)stop {
    // There's an important caveat here: specifying the parameters as 
    // NSInteger allows negative indexes, but limits the method's 
    // (theoretical) use: the maximum size of an NSArray is NSUIntegerMax, 
    // which is quite a bit larger than NSIntegerMax. 
    NSUInteger count = [self count];

    // Due to this caveat, bail if the array is too big.
    if( count >= NSIntegerMax ) return nil;

    // Define default start and stop
    NSInteger defaultStart = 0;
    NSInteger defaultStop = count;

    // Set start to default if not specified
    if( start == WSS_SliceNoIndex ){
        start = defaultStart;
    }
    else {
        // If start is negative, change it to the correct positive index.
        if( start < 0 ) start += count;
        // Correct for out-of-bounds index:
        // If it's _still_ negative, set it to 0
        if( start < 0 ) start = 0;
        // If it's past the end, set it to just include the last item
        if( start > count ) start = count;
    }

    // Perform all the same calculations on stop
    if( stop == WSS_SliceNoIndex ){
        stop = defaultStop;
    }
    else {
        if( stop < 0 ) stop += count;
        if( stop < 0 ) stop = 0;
        if( stop > count ) stop = count;
    }

    // Calculate slice length with corrected indexes
    NSInteger sliceLength = stop - start;

    // If no slice, return a new empty array
    if( sliceLength <= 0 ){
        return [NSArray array];
    }
    else {
        return [self subarrayWithRange:(NSRange){start, sliceLength}];
    }

}
@end

*Therefore I think I need to include a link to the Python License and also note that this may still be “Copyright © 2001-2010 Python Software Foundation; All Rights Reserved”, because although this looks to me like a separately-copyrightable derivative work, I ain't a lawyer.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜