views:

41

answers:

1

Hi,

I am making an application that represents a cell phone bill using Core Data. I have three entities: Bill, Line, and Calls. Bills can have many lines, and lines can have many calls. All of this is set up with relationships. Right now, I have a table view that displays all of the bills. When you double click on a bill, a sheet comes down with a popup box that lists all of the lines on the bill. Below the popup box is a box that has many labels that display various information about that line. Below that information I want to list the top 5 numbers called by that line in that month. Lines has a to-many relationship with Calls, which has two fields, number and minutes. I have all of the calls for the selected line loaded into an NSArrayController with a sort descriptor that properly arranges the values. How do I populate 5 labels with the top 5 values of this array controller?

EDIT: The array of calls is already unique, when I gather the data, I combine all the individual calls into total minutes per number for each month. I just need to sort and display the first 5 records of this combined array.

+1  A: 

I may be wrong (and really hope I am), but it looks like you'll need to use brute force on this one. There are no set / array operators that can help, nor does NSPredicate appear to help.

I think this is actually a bit tricky and it looks like you'll have to do some coding. The Core Data Programming Guide says:

If you execute a fetch directly, you should typically not add Objective-C-based predicates or sort descriptors to the fetch request. Instead you should apply these to the results of the fetch. If you use an array controller, you may need to subclass NSArrayController so you can have it not pass the sort descriptors to the persistent store and instead do the sorting after your data has been fetched.

I think this applies to your case because it's important to consider whether sorting or filtering takes place first in a fetch request (when the fetch requests predicate and sort descriptors are set). This is because you'll be tempted to use the @distinctUnionOfObjects set/array operator. If the list is collapsed to uniques before sorting, it won't help. If it's applied after sorting, you can just set the fetch request's limit to 5 and there're your results.

Given the documentation, I don't know that this is how it will work. Also, in this case, it might be easier to avoid NSArrayController for this particular problem and just use NSTableViewDataSource protocol, but that's beyond the scope of this Q&A.

So, here's one way to do it:

  1. Create a predicate to filter for the selected bill's line items.*
  2. Create a sort descriptor to sort the line items by their telephone number (which are hopefully in a standardized format internally, else trouble awaits) via @"call.number" in your case.
  3. Create a fetch request for the line item entity, with the predicate and sort descriptors then execute it**.

With those sorted results, it would be nice if you could collapse and "unique" them easily, and again, you'll be tempted to use @distinctUnionOfObjects. Unfortunately, set/array operators won't be any help here (you can't use them directly on NSArray/NSMutableArray or NSSet/NSMutableSet instances). Brute force it is, then.

I'd create a topFive array and loop through the results, adding the number to topFive if it's not there already, until topFive has 5 items or until I'm out of results.

Displaying it in your UI (using Bindings or not) is, as I said, beyond the scope of this Q&A, so I'll leave it there. I'd LOVE to hear if there's a better way to do this - it's definitely one of those "looks like it should be easy but it's not" kind of things. :-)

*You could also use @unionOfObjects in your key path during the fetch itself to get the numbers of the calls of the line items of the selected bill, which would probably be more efficient a fetch, but I'm getting tired of typing, and you get the idea. ;-)

**In practice I'd probably limit the fetch request to something reasonable - some bills (especially for businesses and teenagers) can be quite large.

Joshua Nozzi
Thanks for your response, however, I think I was a little unclear in my question. I've already solved the problem of the calls being uniqu. That is taken care of when the data is gathered from the bills and stored in Core Data. What I have is an NSArrayController that contains a sorted array of the total minutes for each number called. My real problem is how to display the first through fifth items of the sorted NSArrayController. Coincidentally, your response (esp. @distinctUnionOfObjects) turns out to be quite helpful for another part of the project I was having trouble with, so, thank you.
Kyle
Oh. :-D In this case, I think you'll still need to perform a manual fetch request. NSFetchRequest lets you set the limit. NSArrayController, sadly, is missing this (IMO) basic feature.
Joshua Nozzi