简体   繁体   中英

Objective-C Split an array into two separate arrays based on even/odd indexes

I have an array, NSMutableArray *stringArray that looks like this

stringArray = 

[0]String1
[1]String2
[2]String3
[3]String4
[4]String5
[5]String6

How would I go about splitting this array into two arrays based on even/odd indexes?

Example:

NSMutableArray *oddArray = ([1], [3], [5]);

NSMutableArray *evenArray = ([0], [2], [4]);

Thanks in advance!

create two mutable arrays, use enumerateObjectsWithBlock: on the source array and check idx % 2 to put it into first or second array

Using the ternary operator:

NSArray *array = @[@1,@2,@3,@4,@5,@6,@7,@8,@9,@10,@11,@12];
NSMutableArray *even = [@[] mutableCopy];
NSMutableArray *odd = [@[] mutableCopy];
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
    NSMutableArray *evenOrOdd = (idx % 2) ? even : odd;
    [evenOrOdd addObject:object];
}];

If you like super compact code you could use the ternary operator like

[((idx % 2) ? even : odd) addObject:object];

If you want to split the array to N arrays, you can do

NSArray *array = @[@1,@2,@3,@4,@5,@6,@7,@8,@9,@10,@11,@12];

NSArray *resultArrays = @[[@[] mutableCopy],
                          [@[] mutableCopy],
                          [@[] mutableCopy]];

[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
        [resultArrays[idx % resultArrays.count] addObject:object];
}];

In Objective-C Categories should come to your mind to create re-uasable code:

@interface NSArray (SplittingInto)
-(NSArray *)arraysBySplittingInto:(NSUInteger)N;
@end

@implementation NSArray (SplittingInto)
-(NSArray *)arraysBySplittingInto:(NSUInteger)N
{
    NSAssert(N > 0, @"N cant be less than 1");
    NSMutableArray *resultArrays = [@[] mutableCopy];
    for (NSUInteger i =0 ; i<N; ++i) {
        [resultArrays addObject:[@[] mutableCopy]];
    }

    [self enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
                                [resultArrays[idx% resultArrays.count] addObject:object];
                            }];
    return resultArrays;
}
@end

Now you can do

NSArray *array = [@[@1,@2,@3,@4,@5,@6,@7,@8,@9,@10,@11,@12] arraysBySplittingInto:2];

array contains

(
        (
        1,
        3,
        5,
        7,
        9,
        11
    ),
        (
        2,
        4,
        6,
        8,
        10,
        12
    )
)

创建两个NSIndexSet ,一个用于偶数索引,一个用于奇数,然后使用objectsAtIndexes:来提取数组的相应切片。

There are following ways you can achieve that:-

The first and second one solution are already mentioned by the above two. Below are the implementation of the same:-

//First Solution
NSArray *ar=@[@"1",@"2",@"3",@"4",@"5"];
NSMutableArray *mut1=[NSMutableArray array];
NSMutableArray *mut2=[NSMutableArray array];
[ar enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
    if (idx%2==0)
    {
        [mut1 addObject:object];
    }
    else
    {
        [mut2 addObject:object];
    }
}];

//Second Solution
NSMutableIndexSet *idx1 = [NSMutableIndexSet indexSet];
NSMutableIndexSet *idx2 = [NSMutableIndexSet indexSet];
for (NSUInteger index=0; index <ar.count(); index++)
{
   if(index%2==0)
    {
        [idx1 addIndex:index];
    }
    else{
        [idx2 addIndex:index];
    }
}
NSArray *evenArr=[ar objectsAtIndexes:idx1];
NSArray *oddArr=[ar objectsAtIndexes:idx2];
NSLog(@"%@",evenArr);
NSLog(@"%@",oddArr);

Got some time for benchmarking and it turns out that when the input array has more than 10 million , it's faster to use parallel execution.

Here is the concurrent solution that enumerates the input array twice to prevent race conditions on the output arrays.

static NSArray * concurrent(NSArray *input) {
    NSUInteger capacity = input.count / 2;
    NSArray *split = @[
                       [NSMutableArray arrayWithCapacity:capacity],
                       [NSMutableArray arrayWithCapacity:capacity],
                       ];
    [split enumerateObjectsWithOptions:NSEnumerationConcurrent
                            usingBlock:^(NSMutableArray *output, NSUInteger evenOdd, BOOL *stop) {
                                [input enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
                                    if (index % 2 == evenOdd) {
                                        [output addObject:object];
                                    }
                                }];
                            }];
    return split;
}

I consider this to be the best serial solution , so I used it for benchmarking:

static NSArray * serial(NSArray *input) {
    NSUInteger capacity = input.count / 2;
    NSMutableArray *even = [NSMutableArray arrayWithCapacity:capacity];
    NSMutableArray *odd = [NSMutableArray arrayWithCapacity:capacity];

    [input enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
        NSMutableArray *output = (index % 2) ? odd : even;
        [output addObject:object];
    }];
    return @[ even, odd ];
}

Results

  • 1 million elements
    • Serial: 54.081 ms
    • Concurrent: 65.958 ms (18% worse)
  • 10 million elements
    • Serial: 525.851 ms
    • Concurrent: 412.691 ms ( 27% better )
  • 100 million elements
    • Serial: 5244.798 ms
    • Concurrent: 4137.939 ms ( 27% better )

Average of 5 runs.
Input filled with NSNumbers .
Fastest smallest optimization -Os .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM