Help With "top holdings" Challenge


#1

Hi – I on Chapter 24 of the 2nd Edition which is about Collection Classes. I’m really struggling with the 1st challenge.

The challenge is to add a method to the BNRPortfolio class that returns an NSArray of only the top three most valuable holdings, sorted by current value in dollars.

Q1 – In the employee example, all the sorting was handled in main. Maybe that is throwing me off. The array to be sorted is in the BNRPortfolio Class, but the value to sort on is not a property, it is a result returned by a method of the object held in the array. I’m not really sure where to start. Any assistance would be appreciated.

Q2 – Looking ahead to the second part of the question, is there an elegant way to handle getting the “top 3 values” only? Once the mutable array is sorted, do I just take the first three objects by the index? I can’t think that a predicate & filtering would work for getting the top x values. Again, any assistance would be appreciated!

Thanks!


#2

Some inspiration came and I solved my problem, but still not sure it is the most elegant method. Here is what I did:

I added the following to BNRPortfolio.m (after declaring function in .h of course):

- (NSArray *)top3Holdings
{
    // Sort holdings descending by Value in Dollars, then number of shares for dups
    NSSortDescriptor *vid = [NSSortDescriptor sortDescriptorWithKey:@"valueInDollars" ascending:NO];
    NSSortDescriptor *nos = [NSSortDescriptor sortDescriptorWithKey:@"numberOfShares" ascending:YES];
    [_holdings sortUsingDescriptors: @[vid, nos]];

    // Return an array of the 1st 3 members of the sorted array
    NSIndexSet *top3 = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 3)];
    return [_holdings objectsAtIndexes:top3];
}

And in main.m, I simply added:

       NSLog(@"Top 3 holdings");
        NSArray *top3 = [portfolio top3Holdings];
        NSLog(@"Here are the top 3");
        for (BNRStockHolding *holding in top3) {
            NSLog(@"%@ valued at $%.2f", holding.stockName, [holding valueInDollars]);
        }

Any suggestions for improving the code are welcome! Thanks.


#3

One more improvement… Instead of sorting the actual mutable array just for this method, cast it as an NSArray and use the sortedArrayUsingDescriptors instead of the mutable version sortUsingDescriptors.

- (NSArray *)top3Holdings
{
    // Sort holdings descending by Value in Dollars, then number of shares for dups
    NSSortDescriptor *vid = [NSSortDescriptor sortDescriptorWithKey:@"valueInDollars" ascending:NO];
    NSSortDescriptor *nos = [NSSortDescriptor sortDescriptorWithKey:@"numberOfShares" ascending:YES];
    NSArray *sortedArray = [(NSArray *)_holdings sortedArrayUsingDescriptors: @[vid, nos]];
    
    // Return an array of the 1st 3 members of the sorted array
    NSIndexSet *top3 = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 3)];
    return [sortedArray objectsAtIndexes:top3];
}

#4

[quote]/ Return an array of the 1st 3 members of the sorted array
NSIndexSet *top3 = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 3)];[/quote]

Really like your NSIndexSet solution


#5

This is my solution took me a while but learned a lot :smiley:

BNRPortfolio.m

[code]- (NSArray *)topThreeHoldings
{
if(_holdings)
{
// New mutabel array
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:self.holdings];

    // Sort array
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"valueInDollars" ascending:NO];
    [mutableArray sortUsingDescriptors:@[sortDescriptor]];
    
    // Return top three values
    return [mutableArray subarrayWithRange:NSMakeRange(0, 3)];
}

// return pass
return 0;

}[/code]