How to use NSPredicate to filter NSMutableSet which contains composite object from other Class?

淺唱寂寞╮ 提交于 2019-12-22 18:04:07

问题


Newbie Questions,

I have 3 Class, 3 of them are subclass from NSOBject.

  1. Collection Class, have 2 properties, masterSong as NSMutableSet (strong, nonatomic) and listOfPlaylists as NSMutableArray (strong, nonatomic)

  2. Playlist Class

Have 2 properties, playListName as NSString (copy, nonatomic) and songList as NSMutableArray (strong, nonatomic)

3 . Song Class

Have 4 properties: title,artist,album, playtime as NSString (copy, nonatomic).


masterSong will contain Song object, and listOfPlaylist will contain Playlist Object.

while songList only store reference to Song Object.

I want to create removeSong method for Collection Class by looking up Song for title,artist,or album inside masterSong.

if looking up find 1, it will return NSSet, returned NSSet will take place as parameter in minusSet: method to remove the song from masterSong and all Playlist.songList.

but, I can't figure out how to write down NSPredicate syntax to filter out .title or .album or .artist from masterSong which contain Song Object.

here is what I got so far, for Collection.m

- (void) removeSong: (NSString *)zSong{

    //remove from reference playlist


    NSSet *targets = [self lookUpTitle:zSong];

    if ([targets count]>0) {
        [self.masterSongs minusSet:targets];

        for (Playlist *playlist in listOfPlaylists) {
            // -all objects converts the set into array.

            [playlist.songList removeObjectsInArray:[targets allObjects]];
        }
    }
    else
        ;
}

Xcode throw exception when executing lookUpTitle method by saying that Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't look for value (title:Whole point of no return ,artist: Cafe Bleu ,album: CBSB ,playtime: 3:56) in string (What Do You Know); value is not a string

- (NSSet *) lookUpTitle: (NSString *)aName {

    NSString *filters = @"%K CONTAINS [cd] %@";
    NSPredicate *filter = [NSPredicate predicateWithFormat:filters, @"title", aName];

    NSSet *result = [masterSongs filteredSetUsingPredicate:filter];

    if ([result count] == 0) {
        NSLog(@"not found");

        return nil;
    }
    else{
        return result;
    }
}

I know that the main problem is at lookUpTitle method

 NSPredicate *filter = [NSPredicate predicateWithFormat:filters, @"title", aName];

 NSSet *result = [masterSongs filteredSetUsingPredicate:filter];

the filter is filtering in a wrong way, while it should filtering NSString object (title, artist, or album) but masterSong is containing Song Object, how do I access title,artist,album in Song Object placed inside masterSong with NSPredicate?

any other effective way for this condition?

Predicate Programming Guide only show example set/array containing string

I found other thread which has similar topic, but block is quite confusing to read, and still won't work in my case.


Edit : additions to the partial code (Class.m method) as requested by responder #1 . here are some method in .m files related to my questions

Main.m

    #import <Foundation/Foundation.h>
#import "MusicCollection.h"
#import "Playlist.h"
#import "Song.h"
int main(int argc, const char * argv[])
{

    @autoreleasepool {

        // insert code here...
       // NSLog(@"Hello, World!");



        //create Songs

        Song *aSong = [[Song alloc]initWithTitle:@"Whole point of no return" withArtist:@"Cafe Bleu" withAlbum:@"CBSB" withPlaytime:@"3:56"];
        Song *bSong  = [[Song alloc]initWithTitle:@"Council Meetin'" withArtist:@"Cafe Bleu" withAlbum:@"CBSB" withPlaytime:@"4:00"];

        Song *cSong = [[Song alloc]initWithTitle:@"Hayate" withArtist:@"Spitz" withAlbum:@"Indigo Chiheisen" withPlaytime:@"4:21"];

        Song *dSong = [[Song alloc]initWithTitle:@"I have a Dreams" withArtist:@"WestLife" withAlbum:@"Season" withPlaytime:@"4:11"];

        Song *eSong = [[Song alloc]initWithTitle:@"What Do You Know" withArtist:@"David Choi" withAlbum:@"Tomorrow" withPlaytime:@"3:46"];


        //create playList

        Playlist *playlistA = [[Playlist alloc]initWithName:@"Playlist A"];

        Playlist *playListB = [[Playlist alloc]initWithName:@"Playlist B"];

    //store Song A & B to Playlist A and Song A,B,C to playlist B

        [playlistA addSong:aSong];

        [playlistA addSong:bSong];

        [playListB addSong:aSong];

        [playListB addSong:bSong];

        [playListB addSong:cSong];

//        [playListB removeSong:eSong];


//Creating Master Collection

        MusicCollection *myCollection = [[MusicCollection alloc]initWithName:@"Library"];

        [myCollection addPlaylist:playlistA];
        [myCollection addPlaylist:playListB];
        [myCollection addSong:eSong];
        [myCollection addSong:dSong];

        [myCollection removePlaylist:playListB];
        [myCollection removeSong:aSong];

        NSSet *container2 = [myCollection lookUpTitle:@"What"];

    NSLog(@"%@",container2);


    //    NSLog(@"%@",myCollection);
    }
    return 0;
}

Collection.m

-(instancetype) initWithName: (NSString *)aName{
    self = [super init];
    if (self) {

        self.name           = [NSMutableString stringWithString:aName];
        self.listOfPlaylists = [NSMutableArray array];
        self.masterSongs = [NSMutableSet set];


    }
    return self;
}

-(instancetype) init{

    return  [self initWithName:@""];

}

-(void) addPlaylist: (Playlist *)aPlayList{

    if ([listOfPlaylists containsObject:aPlayList]==YES) {

    }
    else
        [listOfPlaylists addObject:aPlayList];


}

-(void) removePlaylist: (Playlist *)aPlayList{
    if ([listOfPlaylists containsObject:aPlayList]) {

        [listOfPlaylists removeObjectIdenticalTo:aPlayList];
    }

    else{
        ;
    }

}

- (void) displaySong{

    NSLog(@"displaying all song in Collection");

    NSLog(@" %@",self.masterSongs);

}

- (void) addSong :(Song *)aSong{
    if (![masterSongs containsObject:aSong]) {

        [masterSongs addObject:aSong];

    }
}

- (void) removeSong: (NSString *)zSong{

    //remove from reference playlist


    NSSet *targets = [self lookUpTitle:zSong];

    if ([targets count]>0) {
        [self.masterSongs minusSet:targets];

        for (Playlist *playlist in listOfPlaylists) {
            // -all objects converts the set into array.

            [playlist.songList removeObjectsInArray:[targets allObjects]];
        }
    }
    else
        ;
}



 - (NSSet *) lookUpTitle: (NSString *)aName {

    NSString *filters = @"%K CONTAINS [cd] %@";
    NSPredicate *filter = [NSPredicate predicateWithFormat:filters, @"title", aName];

    NSSet *result = [masterSongs filteredSetUsingPredicate:filter];

    if ([result count] == 0) {
        NSLog(@"not found");

        return nil;
    }
    else{
        return result;
    }
}

Playlist.m

- (void) addSong :(Song *)aSong{

    if (![songList containsObject:aSong]) {
        [songList addObject:aSong];
    }

}

- (void) removeSong: (Song *)aSong{
    if ([songList containsObject:aSong]){
    [self.songList removeObjectIdenticalTo:aSong];
    }
}
- (instancetype) initWithName: (NSString *)aPLName{
    self = [super init];

    if (self) {
        self.songList = [NSMutableArray array];
        self.playlistName = aPLName;
    }
    return self;
}

- (instancetype)init{
    return  [self initWithName:@""];
}

Song.m

 - (instancetype) initWithTitle: (NSString *)aTitle withArtist: (NSString *)anArtist withAlbum: (NSString *)aAlbum withPlaytime: (NSString *)playingTime{

    self = [super init];

    if (self) {
        self.artist = [NSMutableString stringWithString:anArtist];
        self.album  = [NSMutableString stringWithString:aAlbum];
        self.title =   [NSMutableString stringWithString:aTitle];
        self.playtime = [NSMutableString stringWithString:playingTime];

    }
       return self;
}

-(instancetype) init{
    return  [self initWithTitle:@"null" withArtist:@"null" withAlbum:@"null" withPlaytime:@"null"];
}

回答1:


You have overloaded the meaning of -removeSong: and it has caused you confusion. In Playlist, -removeSong: takes a Song instance, but in Collection, -removeSong: takes a NSString instance for the song title.

The problem is you pass an instance of a Song to the version which expects an NSString.

- (void)removeSong:(NSString *)zSong
{
    NSSet *targets = [self lookUpTitle:zSong];
    // …
}

Should either be

- (void)removeSong:(Song *)aSong
{
    NSSet *targets = [self lookUpTitle:aSong.title];
    // …
}

or

- (void)removeSongWithTitle:(NSString *)aTitle
{
    NSSet *targets = [self lookUpTitle:aTitle];
    // …
}

I changed the name of the second version of the method to clearly show what the expected parameter is. When you want to use the second version, you would pass the message [myCollection removeSongWithTitle:aSong.title]




回答2:


You don't need to use -predicateWithBlock, it should be sufficient to use the following predicate in your case

[NSPredicate predicateWithFormat:@"SELF.title CONTAINS %@", @"Layla"];

EDIT

Jeffery Thomas is right, the problem is in this line - in fact the compiler gives a warning

 [myCollection removeSong:aSong];

you're passing to the -removeSong: a Song object, not the song's title. If you want to pass to -removeSong: a Song object changes the method signature from - (void)removeSong:(NSString *)songTitle to -(void)removeSong:(Song*)song and change accordingly the method implementation to pass to the predicate the song's title which is a NSString

- (void) removeSong: (Song *)zSong {

    //remove from reference playlist
    NSSet *targets = [self lookUpTitle:zSong.title];

    if ([targets count]>0) {
        [self.masterSongs minusSet:targets];

        for (Playlist *playlist in self.listOfPlaylists) {
            // -all objects converts the set into array.

            [playlist.songList removeObjectsInArray:[targets allObjects]];
        }
    }
}

From now on, please do not ignore compiler warnings to avoid passing days debugging this kind of subtle errors.



来源:https://stackoverflow.com/questions/21962546/how-to-use-nspredicate-to-filter-nsmutableset-which-contains-composite-object-fr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!