This may be a basic question but, I have a play that is in xml format. I want to grab the speaker and the lines the speaker has in a dictionary in order to add it to an array. Here is the format
<SPEECH>
<SPEAKER>Narrator</SPEAKER>
<LINE>Two households, both alike in dignity,</LINE>
<LINE>In fair Verona, where we lay our scene,</LINE>
<LINE>From ancient grudge break to new mutiny,</LINE>
<LINE>Where civil blood makes civil hands unclean.</LINE>
<LINE>From forth the fatal loins of these two foes</LINE>
<LINE>A pair of star-cross'd lovers take their life;</LINE>
<LINE>Whole misadventured piteous overthrows</LINE>
<LINE>Do with their death bury their parents' strife.</LINE>
<LINE>The fearful passage of their death-mark'd love,</LINE>
<LINE>And the continuance of their parents' rage,</LINE>
<LINE>Which, but their children's end, nought could remove,</LINE>
<LINE>Is now the two hours' traffic of our stage;</LINE>
<LINE>The which if you with patient ears attend,</LINE>
<LINE>What here shall miss, our toil shall strive to mend.</LINE>
</SPEECH>
So I want to grab the Narrator as the speaker and the lines he/she has and add it to the dictionary. After that I want to add the dictionary to the array and then clear the dictionary.
How can I do this?
Thanks
I'm inferring from one of the original tags in your question, xcode, that you're doing in this in Objective-C. I'll further assume that you wanted to use NSXMLParser.
So, let's assume you have (a) a mutable array of speeches; (b) a mutable dictionary for the current speech; (c) a mutable array of lines for each speech; and (d) a mutable string, value, which will capture the characters found between the start of an element name and the end of that element name.
Then, you have to implement the NSXMLParserDelegate methods. For example, as you're parsing, in your didStartElement, if you encounter a speech element name, you create a dictionary:
if ([elementName isEqualToString:#"SPEECH"]) {
speech = [[NSMutableDictionary alloc] init];
lines = [[NSMutableArray alloc] init];
}
else
{
value = [[NSMutableString alloc] init];
}
As you encounter characters in foundCharacters, you'd append these to the value:
[value appendString:string];
And, in your didEndElement, if you encounter speaker, set it, if you encounter a line, add it, and if you encounter the SPEECH closing tag, go ahead and add the speech (with its SPEAKER and LINES to your array of speeches:
if ([elementName isEqualToString:#"SPEAKER"]) {
[speech setObject:value forKey:#"SPEAKER"];
}
else if ([elementName isEqualToString:#"LINE"]) {
[lines addObject:value];
}
else if ([elementName isEqualToString:#"SPEECH"]) {
[speech setObject:lines forKey:#"LINES"];
[speeches addObject:speech];
speech = nil;
lines = nil;
}
value = nil;
For more information, see the Event-Driven XML Programming Guide or google "NSXMLParser tutorial".
If you use c# and if each SPEECH has only 1 SPEAKER you can do the following
XDocument xdoc = XDocument.Load("XMLFile1.xml");
List<string> lines = xdoc.Descendants("SPEECH").Where(e => e.Element("SPEAKER").Value.ToUpper() == "NARRATOR").Elements("LINE").Select(e => e.Value).ToList();
Related
Im new to xcode and objective c. I have asked this question three times and still can't find a good method or answer. I have several uitextfields that accept user input and adds it to the combinedtextField in order of IBAction used.ie user inputs big in one field,bad in the next one and boy in the third and the result is big bad boy in the combinedtextField.
-(IBAction)addtextField1: (id)sender
{
combinedtextField.text = [NSMutableString stringWithFormat:#"%# %#",
combinedtextField.text,textField1.text];
}
-(IBAction)addtextField2: (id)sender
{
combinedtextField.text = [NSMutableString stringWithFormat:#"%# %#",
combinedtextField.text, textField2.text];
}
-(IBAction)addtextField3:(id)sender
{
combinedtextField.text = [NSMutableString stringWithFormat:#"%# %#",
combinedtextField.text,textField3.text];
}
Now this is where it gets interesting.I need to be able to remove the selected text from the combinedtextField.
-(IBAction)removetextField1:(id)sender
{
//////////????????????////////////////
}
-(IBAction)removetextField2: (id)sender
{
//////////????????????////////////////
}
-(IBAction)removetextField3: (id)sender
{
//////////????????????////////////////
}
If I tap the removetextField2 button it would remove the corresponding text(bad) from the combinedtextField and then it would read (big boy)
Ive looked into nsarrays,nsdictionarys and other methods and have got no where.
Need some way of possibly tagging the input and removing it that way. Example code would be great and very much appreciated.
You've left out some important details about your app, so I'm making some guesses about how it should work.
Perhaps what you should do is keep a mutable array of all of the fragments that have been added to the combined string:
#implementation MyViewController {
NSMutableArray *fragments_;
}
- (void)viewDidLoad {
[super viewDidLoad];
fragments_ = [[NSMutableArray alloc] init];
}
When one of the add buttons is tapped, you append the corresponding field's text to the array and recompute the combined string:
- (IBAction)addTextField1:(id)sender {
[fragments_ addObject:textField1.text];
[self updateCombinedTextField];
}
- (void)updateCombinedTextField {
combinedTextField.text = [fragments componentsJoinedByString:#" "];
}
When one of the remove buttons is tapped, you try to remove the corresponding field's text from the fragments array and recompute the combined string:
- (IBAction)removeTextField1:(id)sender {
[fragments_ removeObject:textField1.text];
[self updateCombinedTextField];
}
That will remove all occurrences of field 1's text from the fragments array. If you just want to remove one instance, you will need to use indexOfObject: (or one of its variants) followed by removeObjectAtIndex:.
You can't do it in a very trivial way (without anything to add, just 1stroke magic function). But, there's the easy way, which i'd possible gone for.
If you don't need to do it a lot of times, and / or the text is quite small (not like a 500 pages book), then:
Create a boolean array named mark, and mark[i] should mark the i'th text field as 'added'. Then, create a function named reloadCombinedTextField, which creates it again, depended of the mark array. (if mark[i] == true, then we add a textfield's text, otherwise not)
Then, just mark or unmark the needed text fields in every function and call the reload function in the end of every call.
Altho, there is another way, but it can be wrong in situations where your text fields have the same text. The thing is, you just search in your combined text field the text from selected text field (for example, with [NSString rangeOfString] method) and remove it.
If you are going to do it often and the text is really big, then it goes much more complicated. But i'm quite sure, that you won't do this on iOS.
I'm using the value of two sliders as the first search criteria.
Now I want a second criteria. I have a
NSMutableArray *suitsArray;
which is containing up to 10 words, or it could be empty (0 words). Example of array:
2012-08-12 03:13:14.825 App[4595:f803] The content of array is(
mushroom,
grill,
pizza
)
What I want is this:
If suitsArray is like above, containing words mushroom, grill and pizza
and the "Suits" value in a dictionary is: "This wine suits mushroom dishes, grilled food, pizza and chicken."
This dictionary is added to searchResultsArray, same with all other dictionaries where "Suits" value containing words mushroom, grill and pizza.
But if suitsArray is empty of objects, skip this critera.
But I'm not sure how to write the code for it. Can you help me on it?
-(IBAction)searchButtonPressed:(id)sender{
resultObjectsArray = [NSMutableArray array];
for(NSDictionary *object in allObjectsArray)
{
NSString *objectPrice = [object objectForKey:#"75 cl price"];
NSString *suitsString = // the words in suitsArray
NSString *objectSuits = [object objectForKey:#"Suits"];
BOOL priceConditionGood = YES;
if (minPrisSlider.value <= maxPrisSlider.value && (winePrice.floatValue < minPrisSlider.value || winePrice.floatValue > maxPrisSlider.value))
priceConditionGood = NO;
if (priceConditionGood)
[resultObjectsArray addObject:object];
}
ResultsTableViewController *nextController = [[self storyboard] instantiateViewControllerWithIdentifier:#"ResultsController"];
nextController.objectsArray = [[NSMutableArray alloc]initWithArray:resultObjectsArray];
[self.navigationController pushViewController:nextController animated:YES];
}
It would be better to see if suitsArray contains the string objectSuits, rather than using rangeOfString.
if ([suitsArray containsObject:objectSuits]);
[resultObjectsArray addObject:object];
After edit: I think I understand now. This method looks at each word in suitsArray, and if it finds any of them in objectSuits, then object is added to the resultObjectArray.
for (NSString *aWord in suitsArray) {
if ([objectSuits rangeOfString:aWord].length == aWord.length) {
[resultObjectArray addObject:objectSuits];
break;
}
}
Second Edit: After further thought, the above code has a flaw in the logic that may or may not be important -- because it uses rangeOfString it will find words inside other words. So, if objectSuits was "This wine goes well with cheesecake" and suitsArray contained the word cheese, it would find a match (I can't imagine a wine that would be good with cheese would be good with cheesecake). So, here is a better solution, I think, that breaks objectSuit up into individual words and puts them into a set. suitsArray is also converted to a set, so that we can use intersectsSet: to find if there are any common words.
NSCharacterSet *sepSet = [NSCharacterSet characterSetWithCharactersInString:#" ,.;"];
NSArray *words = [objectSuits componentsSeparatedByCharactersInSet:sepSet];
NSSet *objectSuitsWords = [NSSet setWithArray:words];
NSSet *suitsSet = [NSSet setWithArray:suitsArray];
BOOL ans = [suitsSet intersectsSet:objectSuitsWords];
So, if ans is 1, then that object should be added to the results array. Notice that sepSet starts with a space and includes a comma, period, and semicolon. There might be other things you might want to include, but I think this should work in most cases.
I am doing an NSLog of an array but instead of data it shows the following values. I do not know how to fix this issue and get the values from the array
if(!surveyQuestions){
surveyQuestions=[[NSMutableArray alloc]init];
}
Total Survey Questions 3
2012-07-31 08:54:53.555 SQL[442:9203] SurveyQuestions (
"<QuestionData: 0x4da10f0>",
"<QuestionData: 0x4b9f120>",
"<QuestionData: 0x4ba42e0>"
)
I'm not sure what you're trying to do but: it's certain that the poor array object has no idea what and how your own custom class does, its best possibility to print an instance of your class is to call its description method, which you see, and which is not really helpful. You maybe want to do two things:
I. If you only want to print your objects like this, override the description method of your class and use some format string (given that you haven't written a single line of code I have to fall back to guess):
- (NSString *)description
{
return [NSString stringWithFormat:#"Name: %#, address: %#", self.name, self.address];
}
II. If you want to use the data of your class elsewhere, you probably want to loop through its properties manually:
for (QuestionData *d in surveyQuestions)
{
NSLog(#"%#", d.name);
// etc.
}
You need to do something like this:
NSArray *theArray = [[NSArray alloc] initWith...];
NSLog(#"array contents: %#", theArray);
I am using the following JSON: http://www.kb.dk/tekst/mobil/aabningstider_en.json
When I try to parse it by the key "location" as such:
// get response in the form of a utf-8 encoded json string
NSString *jsonString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
// get most parent node from json string
NSDictionary *json = [jsonString JSONValue];
// get key-path from jason up to the point of json object
NSDictionary *locations = [json objectForKey:#"location"];
NSLog( #"%#", locations );
// iterate through all of the location objects in the json
for (NSDictionary *loc in locations )
{
// pull library name from the json object
NSString *name = [loc valueForKey:#"name"];
// add library data table arrays respectively
[ libraryNames addObject: ( ( name == nil | name.length > 0 ) ? name : #"UnNamed" ) ];
}
When I print the the object locations via NSLog:
{
address = "Universitetsparken 4, 3. etage, 2100 K\U00f8benhavn \U00d8";
desc = "";
lastUpdated = "";
latlng = "55.703124,12.559596";
link = "http://www.farma.ku.dk/index.php?id=3742";
name = "Faculty of Pharmaceutical Sciences Library";
parts = {
part = {
hour = {
day = "5.June Constitution Day (Denmark)";
open = Closed;
};
hours = {
hour = {
day = Friday;
open = "10-16";
};
};
name = main;
};
};
}
Which is only the last value for the "location" keys. Am I doing something wrong?
I tried validating the JSON via http://jsonlint.com/, however when I'd put in the JSON URL as above, it said "valid" - still only the last "locations" key was shown", however if I copy-paste it, it will not validate the JSON, and has to be fixed by removing new-lines from the string.
Also, when i try to parse the JSON and get the "name" fields, I get the following exception:
2012-05-08 15:37:04.941 iPhone App Tabbed[563:f803] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x68bfe70> valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.'
*** First throw call stack:
(0x13dc052 0x156dd0a 0x13dbf11 0x9d2f0e 0x941841 0x940ca9 0x4593 0xf964e 0x114b89 0x1149bd 0x112f8a 0x112e2f 0x1148f4 0x13ddec9 0x365c2 0x3655a 0x25b569 0x13ddec9 0x365c2 0x3655a 0xdbb76 0xdc03f 0xdbbab 0x25dd1f 0x13ddec9 0x365c2 0x3655a 0xdbb76 0xdc03f 0xdb2fe 0x5ba30 0x5bc56 0x42384 0x35aa9 0x12c6fa9 0x13b01c5 0x1315022 0x131390a 0x1312db4 0x1312ccb 0x12c5879 0x12c593e 0x33a9b 0x281d 0x2785)
terminate called throwing an exception(lldb)
It would make more sense if the "locations" tag was an array object enclosed by square brackets ([]), however right now it's only an sequence of normal key-value pairs... Sadly, that's the JSON I have to work with.
Please help and thanks a great deal! :)
Sincerely,
Piotr.
The JSON you've got to work with may be valid, but it doesn't make much sense. It has one big dictionary with the location key repeated many times. Most JSON parser will simply return the last value for the repeated key. It would be best if you could change the structure to use an array instead, but if you cannot there's still hope. You can read the stream and stuff the values from the location keys into an array as they come out of it. This is how you'd do that:
#interface BadJsonHelper : NSObject
#property(strong) NSMutableArray *accumulator;
#end
#implementation BadJsonHelper
- (void)parser:(SBJsonStreamParser *)parser foundArray:(NSArray *)array {
// void
}
- (void)parser:(SBJsonStreamParser *)parser foundObject:(NSDictionary *)dict {
[accumulator addObject:dict];
}
#end
You can drop that little helper class at the top of your file, outside the #implementation section of the class where you're doing your work. (There's no need for the #interface and #implementation being in different files.)
In your code, you would use it like this:
BadJsonHelper *helper = [[BadJsonHelper alloc] init];
helper.accumulator = [NSMutableArray array];
SBJsonStreamParserAdapter *adapter = [[SBJsonStreamParserAdapter new] init];
adapter.delegate = helper;
adapter.levelsToSkip = 1;
SBJsonStreamParser *parser = [[SBJsonStreamParser alloc] init];
parser.delegate = adapter;
switch ([parser parse: responseData]) {
case SBJsonStreamParserComplete:
NSLog(#"%#", helper.accumulator);
break;
case SBJsonStreamParserWaitingForData:
NSLog(#"Didn't get all the JSON yet...");
break;
case SBJsonStreamParserError:
NSLog(#"Error: %#", parser.error);
break;
}
This example was originally adapted from the following test:
https://github.com/stig/json-framework/blob/master/Tests/StreamParserIntegrationTest.m
Update: I created a fully functional example project that loads the JSON asynchronously and parses it. This is available from github.
The JSON is valid, however there is a basic problem regarding the definition of the array of items.
Instead of defining an array of locations using brackets, the JSON redefines the same location key/value pair over and over again. In other words JSON initially says the value of location is the collection with name "The Black Diamond", but immediately after it redefines it with the collection with name "Faculty Library of Humanities" and so on till the last location Faculty of Pharmaceutical Sciences Library".
The same is true for parts and hours.
If you can't fix the result of the JSON and you really need to get it working you may want to modify the JSON removing the "location" keys and adding brackets properly.
Edit
Alternatively you may use an NSScanner and process the JSON result manually. Kinda hacky but it will work as long as the JSON format doesn't change significantly.
Edit
This snipped of code should do the work...
NSString *jsonString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
int indx = 1;
for (;;)
{
NSRange locationRange = [jsonString rangeOfString:#"\"location\":"];
if (locationRange.location == NSNotFound) break;
jsonString = [jsonString stringByReplacingCharactersInRange:locationRange
withString:[NSString stringWithFormat:#"\"location%d\":", indx++]];
}
NSDictionary *locations = [json objectForKey:#"location"];
As you can see, the result of JSON parsing by SBJson is a NSDictionary. A dictionary contains key/value pairs, and the keys are unique identifiers for the pairs.
The JSON data you need to handle is valid but not a good one. Per RFC 4627 - 2.2:
An object structure is represented as a pair of curly brackets surrounding zero or more name/value pairs (or members). A name is a string. A single colon comes after each name, separating the name from the value. A single comma separates a value from a following name. The names within an object SHOULD be unique.
Things like jQuery can parse the JSON also, but the result is the same as SBJson (the last one as the one). See Do JSON keys need to be unique?.
It is not a MUST, but it's still not a good practice. It would be much easier if you are able to change the structure of the JSON data on the server side (or even on the client side after receiving it) rather than parsing it as is.
I need a (quick and dirty) solution to basically detect if a certain NSString is a 'real' word, that is, if it's in the dictionary. So basically, a very simplistic spell checker. Does anyone know of any way to do this? Basically I either need a file containing all words in the English dictionary (which I've searched for, but to no avail), or a way to interface with the iPhones spell checking service. Of course I would like to interface with the iPhones spell check service in a similar way to NSSpellChecker on OSX so my app will work with other languages, but at this point I'll take what I can get.
Lastly, here's some pseudo-code to better illustrate my needs:
-(BOOL)isDictionaryWord:(NSString*)word; //returns TRUE when word=#"greetings". returns FALSE when word=#"slkfjsdkl";
Use UITextChecker instead. The code below might not be perfect but should give you a good idea.
-(BOOL)isDictionaryWord:(NSString*)word {
UITextChecker *checker = [[UITextChecker alloc] init];
NSLocale *currentLocale = [NSLocale currentLocale];
NSString *currentLanguage = [currentLocale objectForKey:NSLocaleLanguageCode];
NSRange searchRange = NSMakeRange(0, [word length]);
NSRange misspelledRange = [checker rangeOfMisspelledWordInString:word range:searchRange startingAt:0 wrap:NO language:currentLanguage];
return misspelledRange.location == NSNotFound;
}
You can make UITextChecker work accurately without needing to add a new dictionary.
I use a two-step process because I need the first step to be fast (but not accurate). You may only need step two which is the accurate check. Note this makes use of the UITextChecker's completionsForPartialWordRange function which is why it's more accurate than the MisspelledWord function.
//Step one: I quickly check to see if a combination of letters passes the spell check. This is not that accurate but it's very fast so I can quickly exclude lots of letter combinations (brute force approach).
UITextChecker *checker;
NSString *wordToCheck = #"whatever"; // The combination of letters you wish to check
// Set the range to the length of the word
NSRange range = NSMakeRange(0, wordToCheck.length - 1);
NSRange misspelledRange = [checker rangeOfMisspelledWordInString:wordToCheck range: range startingAt:0 wrap:NO language: #"en_US"];
BOOL isRealWord = misspelledRange.location == NSNotFound;
// Call step two, to confirm that this is a real word
if (isRealWord) {
isRealWord = [self isRealWordOK:wordToCheck];
}
return isRealWord; // if true then we found a real word, if not move to next combination of letters
// Step Two: Extra check to make sure the word is really a real word. returns true if we have a real word.
-(BOOL)isRealWordOK:(NSString *)wordToCheck {
// we dont want to use any words that the lexicon has learned.
if ([UITextChecker hasLearnedWord:wordToCheck]) {
return NO;
}
// now we are going to use the word completion function to see if this word really exists, by removing the final letter and then asking auto complete to complete the word, then look through all the results and if its not found then its not a real word. Note the auto complete is very acurate unlike the spell checker.
NSRange range = NSMakeRange(0, wordToCheck.length - 1);
NSArray *guesses = [checker completionsForPartialWordRange:range inString:wordToCheck language:#"en_US"];
// confirm that the word is found in the auto-complete list
for (NSString *guess in guesses) {
if ([guess isEqualToString:wordToCheck]) {
// we found the word in the auto complete list so it's real :-)
return YES;
}
}
// if we get to here then it's not a real word :-(
NSLog(#"Word not found in second dictionary check:%#",wordToCheck);
return NO;
}