Filter XML results before display

Discussion in 'iOS Programming' started by bmasar, Apr 8, 2013.

  1. bmasar macrumors newbie

    Joined:
    Apr 8, 2013
    #1
    In essence, this is a sports scoreboard app for iOS. I need to allow users to select a value for "Round", then display only the cells with that value. (For reference, currently my table returns every game of the season so far, I'd like to display them one round at a time.)

    I wasn't having any success with simple things like if statements, so I'm turning to you guys.

    Here's my code,

    XMLParser.m:

    Code:
    #import "XMLParser.h"
    
    @implementation XMLParser
    @synthesize matchups = _matchups;
    
    NSMutableString *currentNodeContent;
    NSXMLParser     *parser;
    Soccer          *currentMatchup;
    bool            isStatus;
    
    -(id) loadXMLByURL:(NSString *)urlString
    {
      _matchups = [[NSMutableArray alloc] init];
      NSURL *url = [NSURL URLWithString:urlString];
      NSData *data = [[NSData alloc] initWithContentsOfURL:url];
      parser = [[NSXMLParser alloc] initWithData:data];
      parser.delegate = self;
      [parser parse];
      return self;
    }
    
    -(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    {
      currentNodeContent =(NSMutableString *) [string stringByTrimmingCharactersInSet:       [NSCharacterSet whitespaceAndNewlineCharacterSet]];
    }
    
    -(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
    {
      if ([elementName isEqualToString:@"Date"]) 
      {
          currentMatchup = [Soccer alloc];
          isStatus = YES;
      }
    }
    
    -(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    {
      if (isStatus) {
          if ([elementName isEqualToString:@"Round"])
          {
              currentMatchup.round = currentNodeContent;
          }
          if ([elementName isEqualToString:@"HomeTeam"])
          {
              currentMatchup.homeTeam = currentNodeContent;
          }
      // etc. etc., there are a lot of elements loaded
      }
    
     if ([elementName isEqualToString:@"Match"])
      {
          [self.matchups addObject:currentMatchup];
          if (currentMatchup.timeElapsed == nil)
          {
              currentMatchup.timeElapsed = @"F";
          }
          currentMatchup = nil;
          currentNodeContent = nil;
      }
    }
    
    @end
     
  2. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #2
    What do you expect to have happen? What would you like to have happen? What is happening? How do the three differ from each other (if at all?)
     
  3. bmasar thread starter macrumors newbie

    Joined:
    Apr 8, 2013
    #3
    The XML parser is loading fine, but it is outputting the score of every soccer game soccer played so far this season in Scotland. I would like to display the score one round at a time. "Round" is an element (numbered 1-22) within the parent node in the XML source I'm grabbing. There are also several other elements for each game (HomeTeam, AwayTeam, HomeGoals, AwayGoals, etc.)

    I was hoping I could solve the problem by inserting an if statement along these lines:
    Code:
    if ([elementName isEqualToString:@"Round"])
    {
         currentMatchup.round = currentNodeContent;
             if ([currentMatchup.round = selectedRound])
             {
                  Load the rest of the games for that round
             }
    }
    In the above case, selectedRound would be a string whose value would be pre-determined with a picker.

    The problem with the code above was that the array wouldn't get filled at all. After some research, I discovered that I would probably need to sort my array using a more sophisticated approach. That's where I'm at right now, because I'm still learning the ropes.
     
  4. waterskier2007 macrumors 68000

    waterskier2007

    Joined:
    Jun 19, 2007
    Location:
    White Lake, MI
    #4
    Why not apply the filtering after you have parsed the xml. I am assuming that your xml parses out into an array of "games". Why not filter the array after it is parsed out. You could use something like

    Code:
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"round == %@", selectedValue];
    NSArray *filteredArray = [baseArray filteredArrayUsingPredicate:pred];
    
    This is assuming your "games" class has a property called round

    Note: This is just from memory but you can consult the predicate and array class documentation
    NSPredicate documentation

    NSArray documentationl
     
  5. bmasar thread starter macrumors newbie

    Joined:
    Apr 8, 2013
    #5
    I've been tinkering with this for a bit and I can't get it to work right. This is what I've got so far:
    Code:
    - (void)filterResultsByRound
    {
        NSPredicate *pred = [NSPredicate predicateWithFormat:@"round == %@",roundSelected];
        NSArray *filteredArray = [matchups filteredArrayUsingPredicate:pred];
    }
    but no matter what I put for my baseArray, I get an error "Use of undeclared identifier". I can't figure out why it isn't working, matchups is called within the same implementation file several times without issues. For example to load my table:

    Code:
    Soccer *currentMatchup = [[xmlParser matchups] objectAtIndex:indexPath.row];
    and for numberOfRowsInSection:

    Code:
    return [[xmlParser matchups] count];
    Just in case, here is the property:

    Code:
    @property (strong, readonly) NSMutableArray *matchups;
    and it does get synthesized. Any thoughts?
     
  6. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #6
    Which line is giving you this error?
     
  7. bmasar thread starter macrumors newbie

    Joined:
    Apr 8, 2013
    #7
    Code:
    NSArray *filteredArray = [matchups filteredArrayUsingPredicate:pred];
    and it's referring to matchups, which is the name of the NSMutableArray that my parser is loading my info into. Yet I'm getting that error, and I don't know why. The same array (matchups) is being called successfully two lines below it when creating my tableView.
     
  8. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #8
    You need to specify the name of the round that you want to display. That's what the roundSelected parameter is. That's the cause of the syntax error.
     
  9. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #9
    matchups is a property. You should be using the getter for it. (i.e. self.matchups).
     
  10. bmasar thread starter macrumors newbie

    Joined:
    Apr 8, 2013
    #10
    roundSelected is specified and given a value elsewhere. matchups is getting the error.
     
  11. bmasar thread starter macrumors newbie

    Joined:
    Apr 8, 2013
    #11
    I'm sorry, I don't follow.
     
  12. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #12
    How much do you know about using properties and the accessors (getters and setters) that the compiler creates for you?
     
  13. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #13
    Although you should probably go through the accessor methods like dejo suggested, you could also use the ivar directly, _matchups (note the underscore at the front of the name.)

    You should still learn about properties, accessors (getters and setters), ivars, and the differences between all of them, though,
     
  14. waterskier2007 macrumors 68000

    waterskier2007

    Joined:
    Jun 19, 2007
    Location:
    White Lake, MI
    #14
    Your predicate should be

    Code:
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"self.round == %@",roundSelected];
    
     
  15. bmasar thread starter macrumors newbie

    Joined:
    Apr 8, 2013
    #15
    I've tried all of these options, there must be something else wrong that I need to get to the bottom of. I'm getting an empty filteredArray.
     
  16. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #16
    Did you fix your error? If so, how? Have you done any debugging? If so, what? For example, how many objects are in matchups when you call filteredArrayUsingPredicate: on it?
     
  17. bmasar thread starter macrumors newbie

    Joined:
    Apr 8, 2013
    #17
    Yes, the error is gone, but I'm not sure if I really solved that problem. I declared and synthesized NSMutableArray matchups in ButtonViewController. But it had already been declared and synthesized in my XMLParser object, and the parser object is imported into ButtonViewController. So I thought it shouldn't need to be declared and synthesized again, correct?

    So perhaps my NSDelegate method is reading from an empty array, which is why filteredArray isn't being filled. Or it could be something wrong with the filteredArray, or the filter criteria, or whatever else. This whole exercise is extremely frustrating because I don't know any programers personally to sit down with me and point things out.

    To answer your other question, the parser loads 192 total cells, one for each individual soccer game, and each game has 13 elements loaded.

    192 parent nodes, 13 elements within each node being grabbed.
     
  18. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #18
    Correct. My apologies, I misunderstood your earlier post. I thought you already had matchups as a property in your ButtonViewController but, now that I've looked at it again, I see that you meant it is a property of your XMLParser class. In which case, redeclaring it does no good, unless you plan to assign it a value. You are referencing it using [xmlParser matchups] elsewhere; you could probably get-by doing the same thing for the filtering. However, it is probably not a great approach to be constantly referencing the property of another class instance.

    Also, in the future, it might help if you provided more context for each line-of-code by including the entire method or interface they in which they reside, like you helpfully did with your first post.

    Seems like you're the programmer, in this case. :)

    Are you not comfortable with that role?

    That doesn't really answer my question. I was not really asking about what XMLParser does; I was asking about verifying the contents of your matchups variable at the time you call the filteredArrayUsingPredicate: method on it. This could be done with a NSLog call, or even by using a breakpoint at that line, and using the debugger panel to examine the matchups variable/property.
     
  19. bmasar, Apr 11, 2013
    Last edited: Apr 11, 2013

    bmasar thread starter macrumors newbie

    Joined:
    Apr 8, 2013
    #19
    I am comfortable being the programmer because its my pet project. I'd much rather accomplish this goal on my own, but it can be frustrating at times.

    Anywho, just to be clear, matchups is the name of the NSMutableArray, currentMatchup is the name used to access each game, and round is the element to be filtered based on value.

    The array is working because I output all the games successfully in the ButtonViewController, and I've specifically tested round to make sure it is holds data.
     
  20. dejo, Apr 11, 2013
    Last edited: Apr 11, 2013

    dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #20
    Alright, so what isn't working right now? What does your current code look like for filtering? Where have you placed it? In other words, give us an idea on where you're still stuck. Any information you can provide on debugging you've done is also helpful.
     
  21. bmasar, Apr 11, 2013
    Last edited by a moderator: Apr 11, 2013

    bmasar thread starter macrumors newbie

    Joined:
    Apr 8, 2013
    #21
    So the parser is in my first post.

    Here is my ButtonViewController.m (edited for relevancy):
    Code:
    #import "ButtonViewController.h"
    #import "ViewController.h"
    #import "XMLParser.h"
    
    @interface ButtonViewController ()
    
    @end
    
    @implementation ButtonViewController
    
    XMLParser *xmlParser;
    //various UILabels and Frames
    int tagNumber;
    
    NSString *roundSelected = @"3";
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"About to retrieve matchups");
        xmlParser = [[XMLParser alloc] loadXMLByURL:@"http://masarvideo.com/kids/ss/GetHistoricMatchesByLeagueAndSeason.xml"];
        
        self.title = @"Matchups";
        
        [self filterResultsByRound];
        [self addMyButton];
    	// Do any additional setup after loading the view, typically from a nib.
    }
    
    /*- (void)filterResultsByRound
    {
        NSArray *filteredArray = [[NSArray alloc] init];
        NSPredicate *pred = [NSPredicate predicateWithFormat:@"round == %@", roundSelected];
        filteredArray = [self.matchups filteredArrayUsingPredicate:pred];
    }*/
    
    - (void)addMyButton
    {
        NSInteger buttonCount = [[xmlParser filteredArray] count];
        NSLog(@"array %lu",(unsigned long)[[xmlParser filteredArray] count]);
        int indexNumber = 0;
        
        
        CGRect buttonFrame = CGRectMake(30,5,260,60);
        
        for (int index = 0; index <buttonCount; index++) {
            Soccer *currentMatchup = [[xmlParser filteredArray] objectAtIndex:indexNumber];
            
            //UIButtons (named scoreBox) and UILabels generated and loaded here.
            
            [self.scroller addSubview:scoreBox];
            buttonFrame.origin.y+=buttonFrame.size.height+5.0f;
            indexNumber++;
        }
        
        CGSize contentSize = self.scroller.frame.size;
        contentSize.height = buttonFrame.origin.y;
        [self.scroller setContentSize:contentSize];
        
    }
    
    
    - (void)buttonPressed:(UIButton *)sender
    {
        sender.highlighted = NO;
        sender.selected = !sender.selected;
        tagNumber = sender.tag;
        NSLog(@"tag %d",tagNumber);
    }
    
    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end
    As far as debugging, I've tried various iterations of matchups including self.matchups and _matchups.

    I've tried putting the filtering code in viewDidLoad and as a separate method.

    I've tried creating filteredArray as an NSArray and NSMutableArray.

    Finally, I've tried to address round by trying currentMatchup.round and self.round, as well as trying different combinations of quotation marks (" & ') in the comparison statement, and removing roundSelected and using a simple string ("3").

    In all events, the NSLog(@"array %lu",(unsigned long)[[xmlParser filteredArray] count]) returns 0. The log for xmlParser matchups returns 192.
     
  22. chown33, Apr 11, 2013
    Last edited: Apr 11, 2013

    chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #22
    The parser code in your first post doesn't have a filteredArray property or method. So the code hilited in bold is referring to something you haven't shown yet.

    Please post accurate code for XMLParser, both the .m and .h files.


    What does [xmlParser filteredArray] return? Is it an object or is it nil?

    In Objective-C, sending a message to nil generally returns 0. So it may be the filteredArray is an actual object whose count is 0, or it may be nil and it's returning 0 for that reason.


    In the posted code for XMLParser, you have this:
    Code:
          currentMatchup = [Soccer alloc];
    
    Nowhere do I see a call to an init method, which means it may be wrong (incorrectly or incompletely initialized). Please post accurate code for the Soccer class.
     
  23. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #23
    Hmm, it seems you are just trying fixes without really understanding what kind of a difference they will make. How comfortable are you with the fundamentals of Objective-C programming? For example, can you explain the difference between calling [xmlParser matchups] and using self.matchups (which is really just syntactic-sugar for [self matchups])?
     
  24. bmasar thread starter macrumors newbie

    Joined:
    Apr 8, 2013
    #24
    Correct, filteredArray shouldn't be called by XMLParser, the code I highlighted is incorrect on my part. Read below for more on that.


    Sounds about right, not the correct coding by me.

    That is a bit of a head scratcher that I had to revisit. The basis for my parser came from this tutorial and he didn't initialize either. It seems to work still, but I added it anyways.

    As for the [[xmlParser filteredArray] count], that was an attempt to piggy back on [[xmlParser matchup] count] (which also stems in part from the tutorial linked above) that was correctly creating the proper number of UIButton scoreBox, though admittedly it's probably not the most efficient way.

    I think this really boils down to not understanding why matchups is returning an "undeclared identifier" error in my ButtonViewController when it's declared and synthesized in XMLParser .h and .m, which is then imported into ButtonViewController.

    ----------

    True, I got to the point of frustration and tried plug and play, some of which were suggested in this thread. It's a bit like learning French from a book, then trying to write and essay in French with only spellcheck to help you.

    I do recognize the difference between [xmlParser matchups] and self.matchups, but the latter was still giving me errors unless I re-declared and re-synthesized, which is not the correct thing to do, while the former was working (gleaned from here). Where I'm at in my Objective-C learning, when something works—try to replicate it.
     
  25. dejo, Apr 12, 2013
    Last edited: Apr 12, 2013

    dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #25
    What resources have you been using to "learn French from a book", as it were? That is, how have you been educating yourself on the fundamentals of Objective-C / iOS programming?

    You do? Good. Explain it to me then. Pretend I know less about Objective-C than you.

    It was working? Is it no longer working? Or are you no longer using it? If so, why did you change it?

    Perhaps your next step should be to learn how to set up breakpoints, step-through, -into and -out-of code and learn to use the debugging panel to examine variables' values whilst stepping along.
     

Share This Page