Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a subarray of NSArray using NSRange?

I have an Array with content. as usual it contain 20 objects. I want the same array split into 2 sections in Tableview. I am trying to implement it with NSMake in current array. For example I need get in first tableview section 3 rows and second will contain all the rest (17 rows ).

switch (section) {
        case 0:
            return
            [[array subarrayWithRange:NSMakeRange(3, 8)] count];
            // in this line, it always takes from the first object in array, despite I told hime start from 3 (If I understand right, how to works NSMakeRange)
            break;
        case 1:
            return
            [[array subarrayWithRange:NSMakeRange(9, 19)] count];
            // here my app is crashing with an error 
            //*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSArray subarrayWithRange:]: range {9, 19} extends beyond bounds [0 .. 19]'
        default:
            break;
    }

Does anyone can help me with that?

like image 471
Anton Avatar asked Oct 25 '13 18:10

Anton


3 Answers

NSMakeRange is defined as (startingIndex, length), not (start, end) which it seems like how you are trying to use it.

So if you need the first 3 objects, then the rest it would look like this:

switch (section) {
    case 0:
        // This returns objects 0-2 in the array
        return [array subarrayWithRange:NSMakeRange(0, 3)];
    case 1:
        // This returns objects 3-20 in the array
        return [array subarrayWithRange:NSMakeRange(3, 17)];
    default:
        break;
}

Edit: According to your comment, you are actually looking for the count to return in number of rows in section. Since you are using a fixed number of rows, you can just return the actual number within the case statement.

switch (section) {
    case 0:
        // This returns the count for objects 0-2 in the array
        return 3;
    case 1:
        // This returns the count for objects 3-20 in the array
        return 17;
    default:
        break;
}

You do not actually need to use [subarrayWithRange], nor NSMakeRange. If you do need to at some point reference the actual array, you will get an NSIndexPath object which you can use to get the object from your array. You will need to use the section and row properties.

Edit: NSRange -> NSMakeRange

like image 156
Joel Fischer Avatar answered Oct 29 '22 17:10

Joel Fischer


As others have noted you are using NSRange improperly.

It's definition is

typedef struct _NSRange {
      NSUInteger location;
      NSUInteger length;
} NSRange;

so the second parameter of the struct is the length of the range, not the location of last element as you apparently think.


That being said, what you are doing it's much more complicated than it should be.

What's the purpose of producing a subarray of a known length and then returning the length of the subarray itself? With this in mind:

return [[array subarrayWithRange:NSMakeRange(3, 8)] count];

should be (using NSRange properly)

return [[array subarrayWithRange:NSMakeRange(3, 6)] count];

but it can actually be just

return 6;

or if the range length is a parameter

return length;

Again, there's no need in the world to slice an array and count. The length is known a priori.


So in the context of UITableViewDataSource, you have to

  • return the count for each section in -tableView:numberOfRowsInSection:. Something like

    switch(section) {
        case 0: return 2;
        case 1: return 18;
    }    
    
  • return the actual objects in tableView:cellForRowAtIndexPath:. Something like

    id object = nil;
    switch (indexPath.section) {
        case 0:
            object = self.objects[indexPath.row];
            break;
        case 1:
            object = self.objects[2 + indexPath.row];
            break;
    }
    ...
    

As an extra tip, I would advice using a different notation for building structs

NSMakeRange(0, 42)

can be written

(NSRange){ .location = 0, .length = 42 }

which is much more readable (and less error prone, especially when you are in doubt about the meaning of the parameters).

Even

(NSRange){ 0, 42 }

is acceptable. I think it's better (and shorter) than NSMakeRange, but it loses the benefits or readability.

like image 5
Gabriele Petronella Avatar answered Oct 29 '22 19:10

Gabriele Petronella


Ok I have solve my issue

In my Fetcher class I did

_sectionOne = [news objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 3)]];
_sectionTwo = [news objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(3, 17)]];

then

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 2;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    switch (section) {
        case 0:
            return [_sectionOne count];
            break;
        case 1:
            return [_sectionTwo count];
            break;
        default:
            break;
    }
    return 0;
}

then in method cellForRowAtIndexPath:

 switch (indexPath.section) {
        case 0:
            item = [_sectionOne objectAtIndex:indexPath.row];
            break;
        case 1:
            item = [_sectionTwo objectAtIndex:indexPath.row];
            break;
        default:
            break;
    }

item - it's my NSObject with MVC

So It's working As I wanted :)

Thanks for all trying to help me.

Cheers

like image 1
Anton Avatar answered Oct 29 '22 19:10

Anton