Determing if a NSString has all unique characters - objective-c

(For example, "assdf" and "aash" would be considered false).

I think you stated the problem backwards, but to test if every character in an NSString is unique, I think the following should work. There may be some funny unicode edge cases that don't work, where identical glyphs show up as different code points.
#interface NSString(_uniqueChars)
-(BOOL) isEveryCharacterUnique;
#end
#implementation NSString(_uniqueChars)
-(BOOL) isEveryCharacterUnique
{
NSMutableSet *set = [NSMutableSet setWithCapacity:self.length];
for ( NSUInteger i = 0; i < self.length; ++i )
{
unichar c = [self characterAtIndex:i];
[set addObject:[NSNumber numberWithUnsignedShort:c]];
}
return (set.count == self.length);
}
#end

I also was trying NSCharacterSet but no results. This could be another solution but +1 for a better one.
- (BOOL) isUnique: (NSString *) aString{
int len = (int)aString.length;
for(int i=0;i<len;i++)
{
NSString *tmp = [aString substringWithRange:NSMakeRange(i, 1)];
for(int j=i+1;j<len;j++)
{
if([[aString substringWithRange:NSMakeRange(j, 1)]isEqualToString: tmp])
{
return NO;
}
}
}
return YES;
}

Related

Objective-C: multiple if and return statements with least amount of code

This is a question on how to code this efficiently with as little as code possible. It already works but I need to incoporate the paramenter numberOfShapes. That if 1 returns ■, if 2 ■■, if 3 ■■■ etc...
I could do some extra if statemets and extra return statements. If Square->if number = 1> return ■, if number >2 return ■■ etc.. But that is a whole lot of code for something very simple.
What is the best way of coding this with the least amount of code?
- (NSString *)getShape: (NSNumber *)shape numberOfShapes: (NSNumber *)number
{
if ([shape isEqualToNumber:[NSNumber numberWithInt:SQUARE]]) return #"■";
if ([shape isEqualToNumber:[NSNumber numberWithInt:CIRCLE]]) return #"●";
if ([shape isEqualToNumber:[NSNumber numberWithInt:TRIANGLE]]) return #"▲";
return #"?";
}
Largely a question of taste unless you have some really heavy performance requirements.
One way to do it could be to set up a dictionary with the map from numbers to glyphs.
Assuming that you have a static variable glyphs, initialize it in the class' initialize method:
static NSDictionary *glyphs;
+ (void)initialize
{
glyphs = #{
#(SQUARE):#"■",
#(CIRCLE):#"●",
#(TRIANGLE):#"▲"
};
}
Then all you have to do is:
- (NSString *)shapeForNumber:(NSInteger)shape
{
NSString *glyph = [glyphs objectForKey: [NSNumber numberWithInteger: shape]];
return glyph ? glyph : #"?";
}
- (NSString *)getShape: (NSNumber *)shape numberOfShapes: (NSNumber *)number
{
unsigned shapeInt = [shape unsignedIntValue];
if (shapeInt >= 3)
return #"?";
NSString *shapeStr = [#"■●▲" substringWithRange:NSMakeRange(shapeInt, 1)];
// Add autorelease here, if using MRR...
NSMutableString *result = [[NSMutableString alloc] init];
unsigned numberInt = [number unsignedIntValue];
for (unsigned i = 0; i < numberInt; i++)
[result appendString:shapeStr];
return result;
}
I don't see the point of using NSNumber objects to pass parameters like this, as they can't do anything that a simple NSUInteger or unsigned can, and are more expensive to use.
Is there a demonstrated performance problem with the code? Otherwise, I think a switch is pretty clear. Or, fewer lines and O(1) is ...
// declare this earlier
static NSArray *shapeChars = #[ #"■", #"●" /* etc. */ ];
// then
return [shapeChars objectAtIndex:[shape intValue]];
Enter the concept of loops. Also, why do you use NSNumbers for this? Plain old ints are good enough.
- (NSString *)getShape:(int)shape numberOfShapes:(int)number
{
if (shape == SQUARE]) return [self shapeRepeated:#"■" nTimes:number];
if (shape == CIRCLE]]) return [self shapeRepeated:#"●" nTimes:number];
if (shape == TRIANGLE]]) return [self shapeRepeated:#"▲" nTimes:number];
return #"?";
}
- (NSString *)shapeRepeated:(NSString *)shape nTimes:(int)n
{
return [#"" stringByPaddingToLength:n withString:shape startingAtIndex:0];
}
- (NSString *)getShape: (NSNumber *)shape numberOfShapes: (NSNumber *)number
{
switch([number intValue]): {
case SQUARE: return #"■";
case CIRCLE: return #"●";
case TRIANGLE: return #"▲";
default: return #"?";
}
}
To incorporate "number":
- (NSString *)getShape: (NSNumber *)shape numberOfShapes: (NSNumber *)number
{
NSString* result = nil;
switch([number intValue]): {
case SQUARE: result = #"■"; break;
case CIRCLE: result = #"●"; break;
case TRIANGLE: result= #"▲"; break;
default: result = #"?";
}
NSString* realResult = #"";
for (int i = 0; i < number; i++) {
realResult = [realResult stringByAppendingString:result];
}
return realResult;
}
You could neaten it up by using a char value for result, but I'm too lazy.

Anagram / partial anagram detection algorithm finds incorrect answers

I've written the following method to find out whether a long word contains a shorter word, and the order in which I pass the letters appears to effect the outcome.
I've noticed that if I feed it absconds and bassy it correctly reports NO, but if I alphabetize the letters and give it abcdnoss and abssy, it gives YES. I'm not too sure why this is – can anyone spot the issue?
- (BOOL) does: (NSString* ) longWord contain: (NSString *) shortWord {
while([longWord length] > 0 && [shortWord length] > 0) {
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString: [shortWord substringToIndex: 1]];
if ([longWord rangeOfCharacterFromSet: set].location == NSNotFound) {
return NO;
}
longWord = [longWord substringFromIndex: [longWord rangeOfCharacterFromSet: set].location+1];
shortWord = [shortWord substringFromIndex: 1];
}
return YES;
}
The problem with your algorithm is that this line doesn't work:
longWord = [longWord substringFromIndex: [longWord rangeOfCharacterFromSet: set].location+1];
If the first letter you search is at the end of the long word, then long word becomes an empty string, and you jump out of your loop to YES.
I would use a different algorithm, like this. I think it's easier to see what's going on, and so less prone to errors:
- (BOOL) does: (NSString* ) longWord contain: (NSString *) shortWord {
NSMutableString *longer = [longWord mutableCopy];
for (int i = 0; i<shortWord.length; i++) {
NSString *letter = [shortWord substringWithRange:NSMakeRange(i, 1)];
NSRange letterRange = [longer rangeOfString:letter];
if (letterRange.location != NSNotFound) {
[longer deleteCharactersInRange:letterRange];
}else{
return NO;
}
}
return YES;
}
- (BOOL) does: (NSString* ) longWord contain: (NSString *) shortWord
{
return ([longWord rangeOfString:shortWord].location != NSNotFound);
}

Sort characters in NSString into alphabetical order

I'm trying to re-arrange words into alphabetical order. For example, tomato would become amoott, or stack would become ackst.
I've found some methods to do this in C with char arrays, but I'm having issues getting that to work within the confines of the NSString object.
Is there an easier way to do it within the NSString object itself?
You could store each of the string's characters into an NSArray of NSNumber objects and then sort that. Seems a bit expensive, so I would perhaps just use qsort() instead.
Here it's provided as an Objective-C category (untested):
NSString+SortExtension.h:
#import <Foundation/Foundation.h>
#interface NSString (SortExtension)
- (NSString *)sorted;
#end
NSString+SortExtension.m:
#import "NSString+SortExtension.h"
#implementation NSString (SortExtension)
- (NSString *)sorted
{
// init
NSUInteger length = [self length];
unichar *chars = (unichar *)malloc(sizeof(unichar) * length);
// extract
[self getCharacters:chars range:NSMakeRange(0, length)];
// sort (for western alphabets only)
qsort_b(chars, length, sizeof(unichar), ^(const void *l, const void *r) {
unichar left = *(unichar *)l;
unichar right = *(unichar *)r;
return (int)(left - right);
});
// recreate
NSString *sorted = [NSString stringWithCharacters:chars length:length];
// clean-up
free(chars);
return sorted;
}
#end
I think separate the string to an array of string(each string in the array contains only one char from the original string). Then sort the array will be OK. This is not efficient but is enough when the string is not very long. I've tested the code.
NSString *str = #"stack";
NSMutableArray *charArray = [NSMutableArray arrayWithCapacity:str.length];
for (int i=0; i<str.length; ++i) {
NSString *charStr = [str substringWithRange:NSMakeRange(i, 1)];
[charArray addObject:charStr];
}
NSString *sortedStr = [[charArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] componentsJoinedByString:#""];
// --------- Function To Make an Array from String
NSArray *makeArrayFromString(NSString *my_string) {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < my_string.length; i ++) {
[array addObject:[NSString stringWithFormat:#"%c", [my_string characterAtIndex:i]]];
}
return array;
}
// --------- Function To Sort Array
NSArray *sortArrayAlphabetically(NSArray *my_array) {
my_array= [my_array sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
return my_array;
}
// --------- Function Combine Array To Single String
NSString *combineArrayIntoString(NSArray *my_array) {
NSString * combinedString = [[my_array valueForKey:#"description"] componentsJoinedByString:#""];
return combinedString;
}
// Now you can call the functions as in below where string_to_arrange is your string
NSArray *blowUpArray;
blowUpArray = makeArrayFromString(string_to_arrange);
blowUpArray = sortArrayAlphabetically(blowUpArray);
NSString *arrayToString= combineArrayIntoString(blowUpArray);
NSLog(#"arranged string = %#",arrayToString);
Just another example using NSMutableString and sortUsingComparator:
NSMutableString *mutableString = [[NSMutableString alloc] initWithString:#"tomat"];
[mutableString appendString:#"o"];
NSLog(#"Orignal string: %#", mutableString);
NSMutableArray *charArray = [NSMutableArray array];
for (int i = 0; i < mutableString.length; ++i) {
[charArray addObject:[NSNumber numberWithChar:[mutableString characterAtIndex:i]]];
}
[charArray sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
if ([obj1 charValue] < [obj2 charValue]) return NSOrderedAscending;
return NSOrderedDescending;
}];
[mutableString setString:#""];
for (int i = 0; i < charArray.count; ++i) {
[mutableString appendFormat:#"%c", [charArray[i] charValue]];
}
NSLog(#"Sorted string: %#", mutableString);
Output:
Orignal string: tomato
Sorted string: amoott

Algorithm to find anagrams Objective-C

I've got an algorithm to find anagrams within a group of eight-letter words. Effectively it's alphabetizing the letters in the longer word, doing the same with the shorter words one by one, and seeing if they exist in the longer word, like so:
tower = eortw
two = otw
rot = ort
The issue here is that if I look for ort in eortw (or rot in tower), it'll find it, no problem. Rot is found inside tower. However, otw is not inside eortw (or two in tower), because of the R in the middle. Ergo, it doesn't think two is found in tower.
Is there a better way I can do this? I'm trying to do it in Objective-C, and both the eight-letter words and regular words are stored in NSDictionaries (with their normal and alphabetized forms).
I've looked at various other posts re. anagrams on StackOverflow, but none seem to address this particular issue.
Here's what I have so far:
- (BOOL) doesEightLetterWord: (NSString* )haystack containWord: (NSString *)needle {
for (int i = 0; i < [needle length] + 1; i++) {
if (!needle) {
NSLog(#"DONE!");
}
NSString *currentCharacter = [needle substringWithRange:NSMakeRange(i, 1)];
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString: currentCharacter];
NSLog(#"Current character is %#", currentCharacter);
if ([haystack rangeOfCharacterFromSet:set].location == NSNotFound) {
NSLog(#"The letter %# isn't found in the word %#", currentCharacter, haystack);
return FALSE;
} else {
NSLog(#"The letter %# is found in the word %#", currentCharacter, haystack);
int currentLocation = [haystack rangeOfCharacterFromSet: set].location;
currentLocation++;
NSString *newHaystack = [haystack substringFromIndex: currentLocation];
NSString *newNeedle = [needle substringFromIndex: i + 1];
NSLog(#"newHaystack is %#", newHaystack);
NSLog(#"newNeedle is %#", newNeedle);
}
}
}
If you use only part of the letters it isn't a true anagram.
A good algorithm in your case would be to take the sorted strings and compare them letter by letter, skipping mis-matches in the longer word. If you reach the end of the shorter word then you have a match:
char *p1 = shorter_word;
char *p2 = longer_word;
int match = TRUE;
for (;*p1; p1++) {
while (*p2 && (*p2 != *p1)) {
p2++;
}
if (!*p2) {
/* Letters of shorter word are not contained in longer word */
match = FALSE;
}
}
This is one that approach I might take for finding out if one ordered word contained all of the letters of another ordered word. Note that it won't find true anagrams (That simply requires the two ordered strings to be the same) but this does what I think you're asking for:
+(BOOL) does: (NSString* )longWord contain: (NSString *)shortWord {
NSString *haystack = [longWord copy];
NSString *needle = [shortWord copy];
while([haystack length] > 0 && [needle length] > 0) {
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString: [needle substringToIndex:1]];
if ([haystack rangeOfCharacterFromSet:set].location == NSNotFound) {
return NO;
}
haystack = [haystack substringFromIndex: [haystack rangeOfCharacterFromSet: set].location+1];
needle = [needle substringFromIndex: 1];
}
return YES;
}
The simplest (but not most efficient) way might be to use NSCountedSet. We can do this because for counted sets, [a isSubsetOfSet:b] return YES if and only if [a countForObject:object] <= [b countForObject:object] for every object in a.
Let's add a category to NSString to do it:
#interface NSString (lukech_superset)
- (BOOL)lukech_isSupersetOfString:(NSString *)needle;
#end
#implementation NSString (lukech_superset)
- (NSCountedSet *)lukech_countedSetOfCharacters {
NSCountedSet *set = [NSCountedSet set];
[self enumerateSubstringsInRange:NSMakeRange(0, self.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[set addObject:substring];
}];
return set;
}
- (BOOL)lukech_isSupersetOfString:(NSString *)needle {
return [[needle lukech_countedSetOfCharacters] isSubsetOfSet:[self lukech_countedSetOfCharacters]];
}
#end

Difficulty with getting random words from NSArray

When I Build & Run my application, it will not generate anything. What I have generating are words and after it erases that word and continues until it exhausts all the words and then repopulates the list again. Here is the code:
#implementation randomnumbersViewController
#synthesize words;
#synthesize randomArray;
#synthesize array;
-(IBAction)generateNumber:(id)sender {
NSInteger randomize(id num1, id num2, void *context);
int rand = arc4random() %2;
if (rand)
return NSOrderedAscending;
else
return NSOrderedDescending;
}
- (void)resetRandomArray;
{
[randomArray setArray: array];
[randomArray sortUsingFunction:random context:NULL];
}
- (NSString*) getRandomWord; {
if ([randomArray count] ==0)
return nil;
NSString* result;
NSInteger randomIndex = [[randomArray lastObject] intValue];
[randomArray removeLastObject];
result = [words objectAtIndex:randomIndex];
return result;
}
- (void)buildRandomWordArray
{
NSInteger index;
NSError *theError;
NSString *path = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"text"];
NSString *text = [NSString stringWithContentsOfFile: path
encoding: NSUTF8StringEncoding
error: &theError];
self.words = [text componentsSeparatedByString: #"\n"];
int arraySize = [words count];
self.array = [NSMutableArray arrayWithCapacity:arraySize];
//This code fills "array' with index values from 0 to the number of elements in the "words" array.
for (index = 0; index<arraySize; index++)
[array addObject: [NSNumber numberWithInt: index]];
[self resetRandomArray];
//for (index = 0; index<=arraySize; index++)
// NSLog(# "Random word: %#", [self getRandomWord]);
}
Also a .txt document must be included in the resources folder in for this to work and I do have it there, but nothing. Does anyone have any suggestions as to how I can actually get it to generate the words, or why it isn't working properly?
I don't get how sorting the array ascending or descending is going to shuffle the array, maybe because it doesn't. :) You should use the Fisher–Yates shuffle implemented here: What's the Best Way to Shuffle an NSMutableArray? Import that category, and just call shuffle on the mutable array.