NSPredicate: Fetch one of each kind

2020-05-20 09:16发布

问题:

I want to create an NSFetchRequest for objects like this:

The Object is Car which has an attribute color. I have four cars:

car1.color = red
car2.color = red
car3.color = blue
car4.color = green

I want to create an NSPredicate that selects only one car for each color(it doesn't matter which car it selects.

How can I achieve this?

In fact I'm looking for something similar like a DISTINCT in SQL

回答1:

Core Data does support fetching distinct values. Apple even provides sample code here: http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/fetchExpressions.html


From that page:

Fetching Distinct Values

To fetch the unique values of a particular attribute across all instances of a given entity, you configure a fetch request with the method setReturnsDistinctResults: (and pass YES as the parameter). You also specify that the fetch should return dictionaries rather than managed objects, and the name of the property you want to fetch.

Swift 2.0

let context:NSManagedObjectContext! = <#Get the context#>
let entity = NSEntityDescription.entityForName( "<#Entity name#>",  inManagedObjectContext:context )
let request = NSFetchRequest()
request.entity = entity
request.resultType = .DictionaryResultType
request.returnsDistinctResults = true
request.propertiesToFetch = [ "<#Attribute name#>" ]

var objects:[[String:AnyObject]]?
do
{
    try objects = context.executeFetchRequest( request )
}
catch
{
    // handle failed fetch
}

Obj-C

NSManagedObjectContext *context = <#Get the context#>;  

NSEntityDescription *entity = [NSEntityDescription  entityForName:@"<#Entity name#>" inManagedObjectContext:context];  
NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
[request setEntity:entity]; 
[request setResultType:NSDictionaryResultType];
[request setReturnsDistinctResults:YES]; 
[request setPropertiesToFetch:@[@"<#Attribute name#>"]];  

// Execute the fetch. 

NSError *error = nil; 
id requestedValue = nil; 
NSArray *objects = [context executeFetchRequest:request error:&error]; 
if (objects == nil) {
    // Handle the error. 
} 

(One "gotcha" is that the results will be returned as dictionaries--if you need the objects you can do another fetch to get them by ID)



回答2:

Did you tried to use valueForKeyPath ? You can do something like that in the array that NSPredicate provide to you.

NSArray* distinctColors = [cars valueForKeyPath:@"@distinctUnionOfObjects.color"];

It will return to you the distinct colors that you have, I do not know if it is what you want, but with the colors you can do something like @Anupdas told:



回答3:

If you are targeting iOS 4.0 or later and not using the Core-Data SQL store why not use predicateWithBlock:?

The following will generate the NSFetchRequest you want.

- (NSFetchRequest*) fetchRequestForSingleInstanceOfEntity:(NSString*)entityName groupedBy:(NSString*)attributeName
{
  __block NSMutableSet *uniqueAttributes = [NSMutableSet set];

  NSPredicate *filter = [NSPredicate predicateWithBlock:^(id evaluatedObject, NSDictionary *bindings) {
    if( [uniqueAttributes containsObject:[evaluatedObject valueForKey:attributeName]] )
      return NO;

    [uniqueAttributes addObject:[evaluatedObject valueForKey:attributeName]];
    return YES;
  }];

  NSFetchRequest *req = [NSFetchRequest fetchRequestWithEntityName:entityName];
  req.predicate = filter;

  return req;
}

You could then create a new method to execute the fetch and return the results you want.

- (NSArray*) fetchOneInstanceOfEntity:(NSString*)entityName groupedBy:(NSString*)attributeName
{
  NSFetchRequest *req = [self fetchRequestForSingleInstanceOfEntity:entityName groupedBy:attributeName];

  // perform fetch
  NSError *fetchError = nil;
  NSArray *fetchResults = [_context executeFetchRequest:req error:&fetchError];

  // fetch results
  if( !fetchResults ) {
    // Handle error ...
    return nil;
  }

  return fetchResults;
}


回答4:

try this:

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity =
[NSEntityDescription entityForName:@"Selected_data"
            inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSPredicate *predicatecnName = [NSPredicate predicateWithFormat:@"countries contains[cd] %@", Countryid];
NSPredicate *predicateLn = [NSPredicate predicateWithFormat:@"languages contains[cd] %@", languageid];
NSArray *subPredicates = [NSArray arrayWithObjects:predicatecnName, predicateLn, nil];

NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];


[request setPredicate:predicate];
NSMutableArray *sendfd=[[NSMutableArray alloc] init];
NSError *error;
NSArray *array1 = [self.managedObjectContext executeFetchRequest:request error:&error];


if (array1 != nil) {


    for (Selected_data *sld in array1)
    {

        [sendfd addObject:sld.providers];
    }


}
else {

}
return sendfd;


回答5:

try this one:

    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Cars"];
    NSArray *arrayValues = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

if (arrayValues.count != 0) {
     NSDictionary *result = [NSDictionary dictionaryWithObjects:arrayValues
                           forKeys:[arrayValues valueForKey:@"color"]];
     return [result allValues];
}