Nested NSArray for loop won't loop in nested fashion - objective-c

I've been learning Objective-C for five days and I have only 2 weeks of prior programming experience so please make answers as simple as possible.
I'm doing an exercise in a book that asks me to generate a list of proper names that are also regular words. To do this I have am running a for loop for each proper name from a NSArray proper name object. Within that for loop I have a nested for loop testing each name against each word in an NSArray regular word object using the caseInsensitiveCompare method.
Here is my code:
import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
//Gets the sting with proper names
NSString *propername = [NSString stringWithContentsOfFile:#"/usr/share/dict/propernames" encoding:
NSUTF8StringEncoding error:NULL];
//Gets the string with regularwords
NSString *inpropername = [NSString stringWithContentsOfFile:#"/usr/share/dict/words" encoding:
NSUTF8StringEncoding error:NULL];
NSArray *proper = [propername componentsSeparatedByString:#"/n"];
NSArray *inproper = [inpropername componentsSeparatedByString:#"/n"];
for (NSString *n in proper){
NSLog(#"%#", n);
for(NSString *i in inproper){
NSLog(#"%#", i);
if ([n caseInsensitiveCompare:i] == NSOrderedSame)
{
NSLog(#"Yahooo! Got One! %#", n);
}
}
}
}
return 0;
}
Instead of the for loops running in a nested fashion they are running in a sequential fashion. The output is like this:
Aaron
all the names...
Yvonne
a
all the regular words....
Zyzzogeton
Any ideas for why the nested for loop is not running in a nested fashion?

The code is correct except you are not breaking the files into words since you are using "/n" instead of "\n".
This means that each array contains exactly one element which is a string with all the words in it.

Related

Why does my NSMutableString edit sometimes not work?

I'm trying to repair some mis-numbered movie subtitle files (each sub is separated by a blank line). The following code scans up to the faulty subtitle index number in a test file. If I just 'printf' the faulty old indices and replacement new indices, everything appears just as expected.
//######################################################################
-(IBAction)scanToSubIndex:(id)sender
{
NSMutableString* tempString = [[NSMutableString alloc] initWithString:[theTextView string]];
int textLen = (int)[tempString length];
NSScanner *theScanner = [NSScanner scannerWithString:tempString];
while ([theScanner isAtEnd] == NO)
{
[theScanner scanUpToString:#"\r\n\r\n" intoString:NULL];
[theScanner scanString:#"\r\n\r\n" intoString:NULL];
if([theScanner scanLocation] >= textLen)
break;
else
{ // remove OLD subtitle index...
NSString *oldNumStr;
[theScanner scanUpToString:#"\r\n" intoString:&oldNumStr];
printf("old number:%s\n", [oldNumStr UTF8String]);
NSRange range = [tempString rangeOfString:oldNumStr];
[tempString deleteCharactersInRange:range];
// ...and insert SEQUENTIAL index
NSString *newNumStr = [self changeSubIndex];
printf("new number:%s\n\n", [newNumStr UTF8String]);
[tempString insertString:newNumStr atIndex:range.location];
}
}
printf("\ntempString\n\n:%s\n", [tempString UTF8String]);
}
//######################################################################
-(NSString*)changeSubIndex
{
static int newIndex = 1;
// convert int to string and return...
NSString *numString = [NSString stringWithFormat:#"%d", newIndex];
++newIndex;
return numString;
}
When I attempt to write the new indices to the mute string however, I end up with disordered results like this:
sub 1
sub 2
sub 3
sub 1
sub 5
sub 6
sub 7
sub 5
sub 9
sub 7
sub 8
An interesting observation (and possible clue?) is that when I reach subtitle number 1000, every number gets written to the mutable string in sequential order as required. I've been struggling with this for a couple of weeks now, and I can't find any other similar questions on SO. Any help much appreciated :-)
NSScanner & NSMutableString
NSMutableString is a subclass of NSString. In other words, you can pass NSMutableString at places where the NSString is expected. But it doesn't mean you're allowed to modify it.
scannerWithString: expects NSString. Translated to human language - I expect a string and I also do expect that the string is read-only (wont be modified).
In other words - your code is considered to be a programmer error - you give something to the NSScanner, NSScanner expects immutable string and you're modifying it.
We don't know what the NSScanner class is doing under the hood. There can be buffering or any other kind of optimization.
Even if you will be lucky with the mentioned scanLocation fix (in the comments), you shouldn't rely on it, because the under the hood implementation can change with any new release.
Don't do this. Not just here, but everywhere where you see immutable data type.
(There're situations where you can do it, but then you should really know what the under the hood implementation is doing, be certain that it wont be modified, etc. But generally speaking, it's not a good idea unless you know what you're doing.)
Sample
This sample code is based on the following assumptions:
we're talking about SubRip Text (SRT)
file is small (can easily fit memory)
rest of the SRT file is correct
especially the delimiter (#"\r\n")
#import Foundation;
NS_ASSUME_NONNULL_BEGIN
#interface SubRipText : NSObject
+ (NSString *)fixSubtitleIndexes:(NSString *)string;
#end
NS_ASSUME_NONNULL_END
#implementation SubRipText
+ (NSString *)fixSubtitleIndexes:(NSString *)string {
NSMutableString *result = [#"" mutableCopy];
__block BOOL nextLineIsIndex = YES;
__block NSUInteger index = 1;
[string enumerateLinesUsingBlock:^(NSString * _Nonnull line, BOOL * _Nonnull stop) {
if (nextLineIsIndex) {
[result appendFormat:#"%lu\r\n", (unsigned long)index];
index++;
nextLineIsIndex = NO;
return;
}
[result appendFormat:#"%#\r\n", line];
nextLineIsIndex = line.length == 0;
}];
return result;
}
#end
Usage:
NSString *test = #"29\r\n"
"00:00:00,498 --> 00:00:02,827\r\n"
"Hallo\r\n"
"\r\n"
"4023\r\n"
"00:00:02,827 --> 00:00:06,383\r\n"
"This is two lines,\r\n"
"subtitles rocks!\r\n"
"\r\n"
"1234\r\n"
"00:00:06,383 --> 00:00:09,427\r\n"
"Maybe not,\r\n"
"just learn English :)\r\n";
NSString *result = [SubRipText fixSubtitleIndexes:test];
NSLog(#"%#", result);
Output:
1
00:00:00,498 --> 00:00:02,827
Hallo
2
00:00:02,827 --> 00:00:06,383
This is two lines,
subtitles rocks!
3
00:00:06,383 --> 00:00:09,427
Maybe not,
just learn English :)
There're other ways how to achieve this, but you should think about readability, speed of writing, speed of running, ... Depends on your usage - how many of them are you going to fix, etc.

Objective-C Noob: How to define + call a method in main?

I wrote a method that works inside of an object, but now I want to extract it so that it's just a function. This is my broken command line tool program:
#import <Foundation/Foundation.h>
+ (NSMutableString *)reverseString:(NSString *)originalString {
NSMutableString *reversedString = [[NSMutableString alloc] init];
for (NSInteger i = originalString.length; i > 0; i--) {
[reversedString appendFormat:#"%c", [originalString characterAtIndex:i-1]];
}
return reversedString;
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSString *originalString = #"original string";
NSMutableString *newString = [reverseString:originalString];
NSLog(#"Reversed string: %#", newString);
}
return 0;
}
My question is, how would I call the reverseString method from main()? I don't think I'm defining it properly. Do I have to declare it too? I know that the contents of my method work fine, but I don't know how to define it in a way that allows main to see it.
A "method" is, by definition, part of a class. There are two types, instance methods and class methods. To invoke an instance method, you need, well, an instance of the class. To invoke a class method, you don't need an instance. You can just invoke it directly on a class.
By contrast, there are also "functions". You don't need an instance or a class to invoke a function.
So, it sounds like you want a function. Functions are something that Objective-C inherits from C. The syntax for functions is different from the syntax for methods. Here's how your code might look using a function:
NSMutableString* reverseString(NSString *originalString) {
NSMutableString *reversedString = [[NSMutableString alloc] init];
for (NSInteger i = originalString.length; i > 0; i--) {
[reversedString appendFormat:#"%c", [originalString characterAtIndex:i-1]];
}
return reversedString;
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSString *originalString = #"original string";
NSMutableString *newString = reverseString(originalString);
NSLog(#"Reversed string: %#", newString);
}
return 0;
}
By the way, your code does not "work fine". You can't iterate through a string by what it calls "characters" and treat all of them as independent. What NSString calls "characters" are actually UTF-16 code units. Not all Unicode characters can be expressed as single UTF-16 code units. Some need to use two code units in what's called a surrogate pair. If you split up and reverse a surrogate pair, you'll get an invalid string.
As a separate issue, Unicode has combining marks. For example, "é" can be expressed as U+0065 LATIN SMALL LETTER E followed by U+0301 COMBINING ACUTE ACCENT. Again, if you reorder those "characters", the accent will combine with a different character (or fail to combine at all).
The correct way to iterate through the composed character sequences of a string is to use the -[NSString enumerateSubstringsInRange:options:usingBlock:] method with the NSStringEnumerationByComposedCharacterSequences option.
By "I want to extract it so that it's just a function" you're implicitly saying "I want a C-style function, not an Objective-C class method". C-style functions are declared and called differently (blame history).
static NSMutableString * reverseString(NSString * originalString) {
...
}
...
NSMutableString *newString = reverseString(originalString);

How to read input in Objective-C?

I am trying to write some simple code that searches two dictionaries for a string and prints to the console if the string appears in both dictionaries. I want the user to be able to input the string via the console, and then pass the string as a variable into a message. I was wondering how I could go about getting a string from the console and using it as the argument in the following method call.
[x rangeOfString:"the string goes here" options:NSCaseInsensitiveSearch];
I am unsure as to how to get the string from the user. Do I use scanf(), or fgets(), into a char and then convert it into a NSSstring, or simply scan into an NSString itself. I am then wondering how to pass that string as an argument. Please help:
Here is the code I have so far. I know it is not succinct, but I just want to get the job done:
#import <Foundation/Foundation.h>
#include <stdio.h>
#include "stdlib.h"
int main(int argc, const char* argv[]){
#autoreleasepool {
char *name[100];
printf("Please enter the name you wish to search for");
scanf("%s", *name);
NSString *name2 = [NSString stringWithFormat:#"%s" , *name];
NSString *nameString = [NSString stringWithContentsOfFile:#"/usr/share/dict/propernames" encoding:NSUTF8StringEncoding error:NULL];
NSString *dictionary = [NSString stringWithContentsOfFile:#"/usr/share/dict/words" encoding:NSUTF8StringEncoding error:NULL];
NSArray *nameString2 = [nameString componentsSeparatedByString:#"\n"];
NSArray *dictionary2 = [dictionary componentsSeparatedByString:#"\n"];
int nsYES = 0;
int dictYES = 0;
for (NSString *n in nameString2) {
NSRange r = [n rangeOfString:name2 options:NSCaseInsensitiveSearch];
if (r.location != NSNotFound){
nsYES = 1;
}
}
for (NSString *x in dictionary2) {
NSRange l = [x rangeOfString:name2 options:NSCaseInsensitiveSearch];
if (l.location != NSNotFound){
dictYES = 1;
}
}
if (dictYES && nsYES){
NSLog(#"glen appears in both dictionaries");
}
}
}
Thanks.
Safely reading from standard input in an interactive manner in C is kind of involved. The standard functions require a fixed-size buffer, which means either some input will be too long (and corrupt your memory!) or you'll have to read in a loop. And unfortunately, Cocoa doesn't offer us a whole lot of help.
For reading standard input entirely (as in, if you're expecting an input file over standard input), there is NSFileHandle, which makes it pretty succinct. But for interactively reading and writing like you want to do here, you pretty much have to go with the linked answer for reading.
Once you have read some input into a C string, you can easily turn it into an NSString with, for example, +[NSString stringWithUTF8String:].

Scanning for doublets in an array after equating upper and lower case letters

I'm reading in a file, containing words and names, as a string. Then I'm breaking it into an array of strings. What I want to do is to print out the names that are also words. The words are spelled with only lower case letters and the names has a capital first letter. Thus, I want to order upper and lower cases the same so that Ii then can scan the array and receive the duplicates.
So what I have in my main.m file looks like this now:
int main(int argc, const char * argv[])
{
#autoreleasepool {
// insert code here...
NSString *wordString = [NSString stringWithContentsOfFile:#"/usr/share/dict/words"
encoding:NSUTF8StringEncoding
error:NULL];
NSArray *words = [wordString componentsSeparatedByString:#"\n"];
Everywhere it says I should use a caseIntensiveCompare method, but I don't understand how it works, or how to use it in this particularly case.. When I search for it on google all I get is this:
NSString *aString = #"ABC";
NSString *bString = #"abc";
if ([aString caseInsesitiveCompare: bString]) == NSOrderedSame)
{
//The strings are ordered equal
}
It seems wrong, firstly because I only have the one string, and secondly I want it to actually order them the letters the same, not to check if they are ordered the same..
If someone could give me a hint of how to do this I would be VERY thankful!
Thanks in advance // Bjoern
Not sure whether i have understand your question properly. But how much i understood is you need to first store array string in mutable set then on the basis of that you can compare the existing one to the new one as represent below code. So that like that you can filter your array as well as identify duplicate both words and names. Below assuming words is you array which contains string values. So on the basis of that processing the further code.
NSMutableSet* existing = [NSMutableSet set];
NSMutableArray* newArray = [NSMutableArray
array];
for (id object in words) {
if (![existing containsObject:[[object
name]lowercaseString]) {
[existing addObject:[[object
name]lowercaseString];
[newArray addObject:object];
}
else
{
NSLog(#"duplicate name=%#", [object name]);
}
}
You could try something like this (explanation in the comments):
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
#autoreleasepool {
NSString *wordString = [NSString stringWithContentsOfFile:#"/usr/share/dict/words"
encoding:NSUTF8StringEncoding
error:NULL];
// Get all the words by separating on newlines & convert to lowercase
// Note: Assuming that the list doesn't contain duplicate strings
// (i.e. the same word or name twice)
// If it does, you should separate/add_to_set/get_all_objects/lowercase instead
NSArray *words = [[wordString componentsSeparatedByString:#"\n"]
valueForKey:#"lowercaseString"];
// Create a counted set to keep track of duplicate strings
NSCountedSet *bag = [[NSCountedSet alloc] initWithArray:words];
// Create a mutable set to add only duplicates
NSMutableSet *duplicates = [NSMutableSet setWithCapacity:0];
// Iterate and add words that appear more than once in the counted set
for (NSString *word in bag) {
if ([bag countForObject:word] > 1) {
[duplicates addObject:word];
}
}
NSLog(#"Words: %lu | Unique words: %lu | Duplicates: %lu", words.count, bag.count, duplicates.count);
// Output => Words: 235887 | Unique words: 234372 | Duplicates: 1515
}
}
Now duplicates is a Set of the strings that are both words & names (as per your requirement i.e. they only differ in the capitalization). You can get an array of the words by sending [duplicates allObjects].

Arrays comparison and unusual result

here in this code i have a sentence that i convert it to an array, then i have another array which is a stoplist, i want to filter my sentence by stoplist, means the final sentence should not contain the stoplist elements!
it seems to be so easy, i killed myself to got it worked, but it never worked! gosh !
could you plz tell me what is the problem ?
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *sentence=[NSString stringWithString:#"i want to filter this sentence by stoplist array for my program"];
NSArray *stopList=[[NSArray alloc]initWithObjects:#"an”,#“and”,#“by”,#“for”,#“from”,#“of”,#“the”,#“to”,#“with",nil];
NSArray *query = [sentence componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSMutableArray *finalsentence=[NSMutableArray array];
for (NSString *word in query) { // for each word in the query...
if (![stopList containsObject:word]) {
// ... if the stopList does not contain the word...
[finalsentence addObject:word]; // ...add it to the final sentence
}
}
NSLog(#"%#",finalsentence);
[pool drain];
return 0;
}
Well, one issue is what is "q" in [finalsentence addObject:q]? You have no 'q' defined in your code.
The other issue is it seems you have your test backwards from the description you gave. Your description says you want to add each word to 'finalsentence' if that word in NOT in your stopList. But in code, you're checking to see if [stopList containsObject:[query objectAtIndex:i]] is YES. I think you should be checking for NO there.
Also, your for loop can be expressed probably a bit more clearly using the fast enumeration syntax:
for (NSString *word in query) { // for each word in the query...
if (![stopList containsObject:word]) {
// ... if the stopList does not contain the word...
[finalsentence addObject:word]; // ...add it to the final sentence
}
}
I hope that helps.
EDIT
Here is the output after copying, pasting, running your updated code. Seems to do what you said you wanted, no?
2011-12-13 03:36:55.544 Some6[8082:707] (
i,
want,
filter,
this,
sentence,
stoplist,
array,
my,
program
)