Arrays comparison and unusual result - objective-c

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
)

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.

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].

Nested NSArray for loop won't loop in nested fashion

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.

capitalizedString doesn't capitalize correctly words starting with numbers?

I'm using the NSString method [myString capitalizedString], to capitalize all words of my string.
However capitalization doesn't work very well for words starting with numbers.
i.e. 2nd chance
becomes
2Nd Chance
Even if n is not the first letter of the word.
thanks
You have to roll your own solution to this problem. The Apple docs state that you may not get the specified behavior using that function for multi-word strings and for strings with special characters. Here's a pretty crude solution
NSString *text = #"2nd place is nothing";
// break the string into words by separating on spaces.
NSArray *words = [text componentsSeparatedByString:#" "];
// create a new array to hold the capitalized versions.
NSMutableArray *newWords = [[NSMutableArray alloc]init];
// we want to ignore words starting with numbers.
// This class helps us to determine if a string is a number.
NSNumberFormatter *num = [[NSNumberFormatter alloc]init];
for (NSString *item in words) {
NSString *word = item;
// if the first letter of the word is not a number (numberFromString returns nil)
if ([num numberFromString:[item substringWithRange:NSMakeRange(0, 1)]] == nil) {
word = [item capitalizedString]; // capitalize that word.
}
// if it is a number, don't change the word (this is implied).
[newWords addObject:word]; // add the word to the new list.
}
NSLog(#"%#", [newWords description]);
Unfortunately this seems to be the general behaviour of capitalizedString.
Perhaps a not so nice workaround / hack would be to replace each number with a string before the transformation, and then change it back afterwards.
So, "2nd chance" -> "xyznd chance" -> "Xyznd Chance" -> "2nd Chance"

How to convert text to camel case in Objective-C?

I'm making little utility to help me generate code for an app I'm making. I like to have constants for my NSUserDefaults settings, so that my code is more readable and easier to maintain. The problem is, that making constants for everything takes some time, so I'm trying to write a utility to generate code for me. I'd like to be able to enter a string and have it converted to camel case, like so:
- (NSString *)camelCaseFromString:(NSString *)input{
return inputAsCamelCase;
}
Now, the input string might be composed of multiple words. I'm assuming that I need some sort of regular expression here, or perhaps there is another way to do it. I'd like to input something like this:
#"scrolling direction"
or this:
#"speed of scrolling"
and get back something like this:
kScrollingDirection
or this:
kSpeedOfScrolling
How would you go about removing spaces and replacing the character following the space with the uppercase version?
- (NSString *)camelCaseFromString:(NSString *)input {
return [#"k" stringByAppendingString:[[input capitalizedString] stringByReplacingOccurrencesOfString:#" " withString:#""]];
}
Capitalize each word.
Remove whitespace.
Insert "k" at the beginning. (Not literally, but a simplification using stringByAppendingString.)
The currently accepted answer has a bug. (also pointed out by #jaydee3)
Words already having proper camelCasing, PascalCasing, or capitalized TLA acronyms will have their correct casing "destroyed" by having non-first characters lowercased via the call to capitalizedString.
So "I prefer camelCasing to PascalCasing for variables" would look like "iPreferCamelcasingToPascalcasingForVariables" but according to the question, should be "iPreferCamelCasingToPascalCasingForVariables"
Use the below category on NSString to create non-destructive camel and pascal casing. It also properly leaves ALL_CAPS words/acronyms in place, although that wasn't really part of the orig question.
An acronym as a first word for a camelCased string would be weird. "WTH" becomes "wTH" which looks weird. Anyway, that's an edge case, and not addressed. However, since question asker is prefixing with "k" then it "k IBM" becomes "kIBM" which looks ok to me, but would look be "kIbm" with currently accepted answer.
Use:
NSString *str = #"K computer manufacturer IBM";
NSLog(#"constant: %#", str.pascalCased);
// "constant: kComputerManufacturerIBM"
Category (class extension) code.
#implementation NSString (MixedCasing)
- (NSString *)camelCased {
NSMutableString *result = [NSMutableString new];
NSArray *words = [self componentsSeparatedByString: #" "];
for (uint i = 0; i < words.count; i++) {
if (i==0) {
[result appendString:((NSString *) words[i]).withLowercasedFirstChar];
}
else {
[result appendString:((NSString *)words[i]).withUppercasedFirstChar];
}
}
return result;
}
- (NSString *)pascalCased {
NSMutableString *result = [NSMutableString new];
NSArray *words = [self componentsSeparatedByString: #" "];
for (NSString *word in words) {
[result appendString:word.withUppercasedFirstChar];
}
return result;
}
- (NSString *)withUppercasedFirstChar {
if (self.length <= 1) {
return self.uppercaseString;
} else {
return [NSString stringWithFormat:#"%#%#",[[self substringToIndex:1] uppercaseString],[self substringFromIndex:1]];
}
}
- (NSString *)withLowercasedFirstChar {
if (self.length <= 1) {
return self.lowercaseString;
} else {
return [NSString stringWithFormat:#"%#%#",[[self substringToIndex:1] lowercaseString],[self substringFromIndex:1]];
}
}
#end
Just use: #"This is a sentence".capitalizedString;
Becomes: >> "This Is A Sentence"
Replace spaces and manipulate...
Uppercases first char of word, and lowers the other letters for each word.