Check strings for same characters in Objective-C - objective-c

I have an array of strings, from which I would like to extract only those with unique character sets. (For example, "asdf" and "fdsa" would be considered redundant). This is the method I am currently using:
NSMutableArray *uniqueCharSets = [[NSMutableArray alloc] init];
NSMutableArray *uniqueStrings = [[NSMutableArray alloc] init];
for (NSString *_string in unique) {
NSCharacterSet *_charSet = [NSCharacterSet characterSetWithCharactersInString:_string];
if (![uniqueCharSets containsObject:_charSet]) {
[uniqueStrings addobject:_string];
[uniqueCharSets addObject:_charSet];
}
}
This seems to work, but it's very slow and resource-intensive. Can anyone think of a better way to do this?

Using an NSDictionary, map each string's lexicographically-sorted equivalent to an NSArray of input strings: (e.g. adfs => [afsd, asdf, ...])
Walk through the dictionary, printing out keys (or their values) which only have single-element array values

I just put together a quick example of how I would approach this, but it turns out that it is more, odd, than you first expect. For one, NSCharacterSet doesn't implement equality to check contents. It only uses the pointer value. Based on this your example will NOT work properly.
My approach is to use an NSSet to deal with the hashing of these for us.
#interface StringWrapper : NSObject
#property (nonatomic, copy) NSString *string;
#property (nonatomic, copy) NSData *charSetBitmap;
- (id)initWithString:(NSString*)aString;
#end
#implementation StringWrapper
#synthesize string, charSetBitmap;
- (id)initWithString:(NSString*)aString;
{
if ((self = [super init]))
{
self.string = aString;
}
return self;
}
- (void)setString:(NSString *)aString;
{
string = [aString copy];
self.charSetBitmap = [[NSCharacterSet characterSetWithCharactersInString:aString] bitmapRepresentation];
}
- (BOOL)isEqual:(id)object;
{
return [self.charSetBitmap isEqual:[object charSetBitmap]];
}
- (NSUInteger)hash;
{
return [self.charSetBitmap hash];
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSMutableSet *stringWrappers = [[NSMutableSet alloc] init];
NSArray *strings = [NSArray arrayWithObjects:#"abc",#"aaabcccc",#"awea",#"awer",#"abcde", #"ehra", #"QWEQ", #"werawe", nil];
for (NSString *str in strings)
[stringWrappers addObject:[[StringWrapper alloc] initWithString:str]];
NSArray *uniqueStrings = [stringWrappers valueForKey:#"string"];
NSLog(#"%#", uniqueStrings);
}
return 0;
}
The code is pretty straightforward. We create a container object to cache the results of the character set's bitmap representation. We use the bitmap representation because NSData implements isEqual: appropriately.

The only thing that come in my mind is not to use containsObject: since NSMutableArray is not ordered (in general), we can assume that containsObject simply iterates the array starting from the beginning until he finds the object. This means O(n) (n comparisons in the worst case).
A better solution may consists in keeping the array ordered and use a custom search method using a dichotomic approach. This way you'll have a O(log n) complexity.
Of course, you must take care of keeping your array ordered (much more efficient than add and reorder), so you should use insertObject:atIndex: method to insert the element properly.

Related

Objective-C NSArray remove object duplicates based on function

It is clear from this question that there are many ways to remove duplicates from an NSArray when the array's elements are primitive types, or when the elements are perfect duplicates. But, is there a way to remove duplicates based on a transformation applied to each element, as is permitted in Underscore.js's uniq function, rather than by simply comparing the whole elements? And if a manual implementation would be difficult to optimize, is there an efficient system-provided method (or 3rd party library algorithm) for accomplishing this that I am missing?
A simple approach:
NSMutableArray* someArray = something;
for (int i = someArray.count - 1; i > 0; i--) {
MyObject* myObject = someArray[i];
for (int j = 0; j < i; j++) {
MyObject* myOtherObject = someArray[j];
if ([myObject isSortaEqual:myOtherObject]) {
[someArray removeObjectAtIndex:i];
break;
}
}
}
Yes, it's N-squared, but that's not a biggie unless the array is fairly large.
If you want to redefine what equality means for your objects, then consider overriding -hash and -isEqual:. Then you can create an NSSet from your array if order is irrelevant, or an NSOrderedSet if it is relevant. Here's an example of a Person class where I want the name of the person to determine object equality.
#interface Person
#property (nonatomic, copy) NSString *name;
#end
#implementation Person
- (BOOL)isEqual:(id)object
{
Person *otherPerson = (Person *)object;
return [self.name isEqualToString:otherPerson.name];
}
- (NSUInteger)hash
{
return [self.name hash];
}
#end
Uniquing them now is rather easy:
NSArray *people = ...;
// If ordered is irrelevant, use an NSSet
NSSet *uniquePeople = [NSSet setWithArray:people];
// Otherwise use an NSOrderedSet
NSOrderedSet *uniquePeople = [NSOrderedSet orderedSetWithArray:people];
Absolutely. You are looking for a way to pass your own method for testing for uniqueness (at least, that's what the uniq function you refer to does).
indexesOfObjectsPassingTest: will allow you to pass your own block to determine uniqueness. The result will be an NSIndexSet of all the objects in the array that matched your test. With that you can derive a new array. The block you are passing is roughly equivalent to the Underscore iterator passed to uniq.
The sister method, indexesOfObjectsWithOptions:passingTest: also allows you to specify enumeration options (i.e. concurrent, reverse order, etc.).
As you mention in your question, there are lots of ways to accomplish this. NSExpressions with blocks, Key-value coding collections operators, etc. could be used for this as well. indexesOfObjectsPassingTest: is probably the closest to what you seem to be looking for, though you can do much the same thing (with a lot more typing) using expressions.
I just came up against this problem, so I wrote a category on NSArray:
#interface NSArray (RemovingDuplicates)
- (NSArray *)arrayByRemovingDuplicatesAccordingToKey:(id (^)(id obj))keyBlock;
#end
#implementation NSArray (RemovingDuplicates)
- (NSArray *)arrayByRemovingDuplicatesAccordingToKey:(id (^)(id obj))keyBlock
{
NSMutableDictionary *temp = [NSMutableDictionary dictionaryWithCapacity:[self count]];
for (NSString *item in self) {
temp[keyBlock(item)] = item;
}
return [temp allValues];
}
#end
You can use it like this (this example removes duplicate words, ignoring case):
NSArray *someArray = #[ #"dave", #"Dave", #"Bob", #"shona", #"bob", #"dave", #"jim" ];
NSLog(#"result: %#", [someArray arrayByRemovingDuplicatesAccordingToKey:^(id obj){
return [obj lowercaseString];
}]);
Output:
2015-02-17 17:44:10.268 Untitled[4043:7711273] result: (
dave,
shona,
jim,
bob
)
The 'key' is a block that returns an identifier used to compare the objects. So if you wanted to remove Person objects according to their name, you'd pass ^(id obj){ return [obj name]; }.
This solution is O(n), so is suitable to large arrays, but doesn't preserve order.

How to add all decimal numbers in an NSMutableArray

I have a NSMutableArray which have some NSDecimalNumber in it, like (500,50.80,70,8000)
Now I want to add all those decimal numbers together.
I've tried to use
for (NSDecimalNumber *number in self.numbersArray)
{
NSDecimal *sum += [number decimalValue]
}
But failed.
A simple way to add all NSNumbers in an array is (similar to what #Mahonor said in a comment):
NSArray *myArray = ... // array of NSNumber (or NSDecimalNumber) objects
NSNumber *sum = [myArray valueForKeyPath:#"#sum.self"];
Contrary to what the Collection Operators: sum states, the numbers in the array are not converted to double, but to NSDecimal. Therefore, no precision is lost when adding decimal numbers. Even NSNumber objects which are not decimal numbers are converted to NSDecimal for the addition. The result of the summation is an instance of NSDecimalValue.
I verified (or tried to) that in two different ways. First, I ran this code
NSNumber *a = [NSNumber numberWithDouble:1.2];
NSNumber *b = [NSDecimalNumber decimalNumberWithString:#"-5.7"];
NSArray *myArray = #[a, b];
id sum = [myArray valueForKeyPath:#"#sum.self"];
and activated Objective-C message logging by setting the environment variable "NSObjCMessageLoggingEnabled=YES". As can be seen in the created "/tmp/msgSends-NNNN" file, decimalNumber (and not doubleValue) is sent to both number objects.
Second, I created a custom class implementing both decimalValue and doubleValue, and applied #sum.self to an array of objects of the custom class:
#interface MyClass : NSObject
#property (nonatomic, assign) double value;
#end
#implementation MyClass
- (NSDecimal)decimalValue
{
return [[NSNumber numberWithDouble:self.value] decimalValue];
}
- (double)doubleValue
{
return self.value;
}
#end
MyClass *a = [MyClass new]; a.value = 1.2;
MyClass *b = [MyClass new]; b.value = -5.7;
NSArray *myArray = #[a, b];
id sum = [myArray valueForKeyPath:#"#sum.self"];
By setting breakpoints in both methods, it is seen that only decimalValue is used for the summation (and valueForKeyPath:#"#sum.self" throws an exception if the class does not implement decimalValue).
One can also see that decimalValue is called from
-[NSArray(NSKeyValueCoding) _sumForKeyPath:]
and the assembler code for this method shows that NSDecimalAdd is uses to add the numbers.
Use - (NSDecimalNumber *)decimalNumberByAdding:(NSDecimalNumber *)decimalNumber
Take a look at NSDecimalNumber Class Reference
NSDecimalNumber *lNumber = [NSDecimalNumber zero];
for (NSDecimalNumber *number in self.numbersArray)
{
lNumber = [lNumber decimalNumberByAdding:number];
}
Manohar's suggestion in the comments is not bad. You can indeed use KVC collection operators to make a one-liner out of this: [myArray valueForKeyPath:#"#sum.doubleValue"];, but you potentially lose precision (depending on the numbers you have stored).
You're basically looking for "reduce" functionality; you need to chain calls to decimalNumberByAdding: so that each call has the succeeding element of the array as its argument. Doing this on an NSArray is easy enough, using performSelector:withObject:
#implementation NSArray (Reduce)
- (id)reduceUsingSelector: (SEL)sel
{
id res = [self objectAtIndex:0];
for( id obj in [self subarrayWithRange:(NSRange){1, [self count]-1}] ){
res = [res performSelector:sel withObject:obj];
}
return res;
}
#end
Use this like so: NSDecimalNumber * sum = [myArray reduceUsingSelector:#selector(decimalNumberByAdding:)];
The code you have isn't successful because NSDecimal is a struct, not an object; it shouldn't be declared as a pointer, and if it wasn't, you wouldn't be able to add it. That's not the right route to a solution.

Add a tag to NSMutableArray

Is it possible to set a tag for an NSMutableArray? I have to somehow determine, in an array of arrays, the single array which needs to be rewritten, and if I could just set the tag to that inner array to 1 (or some other number), this would be extremely easy.
Example:
NSMutableArray* outerArray = [NSMutableArray new];
NSMutableArray* innerArray1 = [NSMutableArray new];
NSMutableArray* innerArray2 = [NSMutableArray new];
NSMutableArray* innerArray3 = [NSMutableArray new];
NSMutableArray* innerArray4 = [NSMutableArray new];
[outerArray addObject:innerArray1];
[outerArray addObject:innerArray2];
[outerArray addObject:innerArray3];
[outerArray addObject:innerArray4];
//now let's say innerArray1 needs to be rewritten
//I would like to be able to do this
[innerArray1 setTag:100];
//then later, when I need to determine which of the arrays inside outerArray
//needs to be rewritten, I can just do this
for(NSMutableArray* temp in outerArray) {
if(temp.tag == 100) {
//do what I need to do
}
}
But you can't use setTag: with NSMutableArrays. What would be a workaround?
Arrays are ordered collections, so why don't you just keep track of which index needs to be rewritten.
When something happens such that the array at index 0 (which, in your example, would be innerArray1) of outer array needs to be written, cache index 0 -- as a property if this routine needs to span across separate methods.
Then, when it comes time to do the rewrite, consult the cached index. Retrieve the array to be rewritten like this: NSArray *arrayToRewrite = [outerArray objectAtIndex:cachedIndexToRewrite]; Or access it directly: [[outerArray objectAtIndex:cachedIndexToRewrite] replaceObjectAtIndex:whatever withObject:whatever];
You could use an NSMutableDictionary instead. The "tag" would just be the key and the array would be the value.
Use associated objects. You can even add a category to NSMutableArray that would add a tag property to them.
#interface NSMutableArray (TagExtension)
#property (nonatomic, assign) NSInteger tag;
#end
#implementation NSMutableArray (TagExtension)
#dynamic tag;
static char TagExtensionKey;
-(NSInteger)tag {
NSNumber *ourTag = (NSNumber *)objc_getAssociatedObject(self, &TagExtensionKey);
if( ourTag ) {
return( [ourTag integerValue] );
}
return(0);
}
-(void)setTag:(NSInteger)newTag {
objc_setAssociatedObject(self, &TagExtensionKey, [NSNumber numberWithInteger:newTag], OBJC_ASSOCIATION_RETAIN);
}
#end
See also: How to add properties to NSMutableArray via category extension?
Not sure why a dictionary is a bad idea here… as alternatives, you can:
remember the index
or if each entry is a unique array, you can simply refer to it by pointer:
NSArray * tagged = theArray;
for (NSMutableArray * at in outerArray) {
if (tagged == at) {
//do what I need to do
}
}
Make your inner arrays class variables. Then you can just access them as:
for(NSMutableArray* temp in outerArray) {
if(temp == self.innerArray1) {
//do what I need to do
}

Have trouble appending to an NSMutableString

#interface MainView : UIView {
NSMutableString *mutableString;
}
#property (nonatomic, retain) NSMutableString *mutableString;
#end
#implementation MainView
#synthesize mutableString;
-(void) InitFunc {
self.mutableString=[[NSMutableString alloc] init];
}
-(void) AppendFunc:(*NString) alpha {
[self.mutableString stringByAppendingString:#"hello"];
NSLog(#"the appended String is: %#",self.mutableString);
int len=[self.mutableString length];
}
Hey Everyone,
i just want to know where I am doing wrong ??? . I tried this code but "mutableString" does not append any value (as value of "len"comes '0' and NSLog doesnt print any value for "mutableString"), though i ve searched on net for the solution, people implemented in the same fashion yet i dont know why my code isnt working.
Thanks in Advance
MGD
stringByAppendingString: creates new string. Use appendString: instead:
[mutableString appendString:#"hello"]
1) Your method names violate the naming conventions: use lower case letters to start with.
2) stringByAppendingString returns a new string as a result and doesn't modify your original. You should use [self.mutableString appendString:#"hello"]; instead.
3) Your init method is leaking. You should use mutableString = [[NSMutableString alloc] init]; and not use the dot syntax, as it will be retained otherwise (and a release would be missing).
Oh God, what a mess. The stringByAppendingString does not change the string, it creates and returns a new one:
// Sets str2 to “hello, world”, does not change str1.
NSMutableString *str1 = [NSMutableString stringWithString:#"hello, "];
NSString *str2 = [str1 stringByAppendingString:#"world"];
If you want to change the mutable string itself, use the appendString method:
// Does not return anything, changes str1 in place.
[str1 appendString:#"world"];
Also, this is a leak:
self.mutableString = [[NSMutableString alloc] init];
This is best written as:
mutableString = [[NSMutableString alloc] init];
…because using accessors in init and dealloc is not the best idea.

Removing duplicates from array based on a property in Objective-C

I have an array with custom objects. Each array item has a field named "name". Now I want to remove duplicate entries based on this name value.
How should I go about achieving this?
I do not know of any standard way to to do this provided by the frameworks. So you will have to do it in code. Something like this should be doable:
NSArray* originalArray = ... // However you fetch it
NSMutableSet* existingNames = [NSMutableSet set];
NSMutableArray* filteredArray = [NSMutableArray array];
for (id object in originalArray) {
if (![existingNames containsObject:[object name]]) {
[existingNames addObject:[object name]];
[filteredArray addObject:object];
}
}
You might have to actually write this filtering method yourself:
#interface NSArray (CustomFiltering)
#end
#implementation NSArray (CustomFiltering)
- (NSArray *) filterObjectsByKey:(NSString *) key {
NSMutableSet *tempValues = [[NSMutableSet alloc] init];
NSMutableArray *ret = [NSMutableArray array];
for(id obj in self) {
if(! [tempValues containsObject:[obj valueForKey:key]]) {
[tempValues addObject:[obj valueForKey:key]];
[ret addObject:obj];
}
}
[tempValues release];
return ret;
}
#end
I know this is an old question but here is another possibility, depending on what you need.
Apple does provide a way to do this -- Key-Value Coding Collection Operators.
Object operators let you act on a collection. In this case, you want:
#distinctUnionOfObjects
The #distinctUnionOfObjects operator returns an array containing the distinct objects in the property specified by the key path to the right of the operator.
NSArray *distinctArray = [arrayWithDuplicates valueForKeyPath:#"#distinctUnionOfObjects.name"];
In your case, though, you want the whole object. So what you'd have to do is two-fold:
1) Use #distinctUnionOfArrays instead. E.g. If you had these custom objects coming from other collections, use #distinctUnionOfArray.myCollectionOfObjects
2) Implement isEqual: on those objects to return if their .name's are equal
I'm going to get flak for this...
You can convert your array into a dictionary. Not sure how efficient this is, depends on the implementation and comparison call, but it does use a hash map.
//Get unique entries
NSArray *myArray = #[#"Hello", #"World", #"Hello"];
NSDictionary *uniq = [NSDictionary dictionaryWithObjects:myArray forKeys:myArray];
NSLog(#"%#", uniq.allKeys);
*Note, this may change the order of your array.
If you'd like your custom NSObject subclasses to be considered equal when their names are equal you may implement isEqual: and hash. This will allow you to add of the objects to an NSSet/NSMutableSet (a set of distinct objects).
You may then easily create a sorted NSArray by using NSSet's sortedArrayUsingDescriptors:method.
MikeAsh wrote a pretty solid piece about implementing custom equality: Friday Q&A 2010-06-18: Implementing Equality and Hashing
If you are worried about the order
NSArray * newArray =
[[NSOrderedSet orderedSetWithArray:oldArray] array]; **// iOS 5.0 and later**
It is quite simple in one line
NSArray *duplicateList = ...
If you don't care about elements order then (unordered)
NSArray *withoutDUP1 = [[NSSet setWithArray:duplicateList] allObjects];
Keep the elements in order then (ordered)
NSArray *withoutDUP2 = [[NSOrderedSet orderedSetWithArray:duplicateList] array];
Implement isEqual to make your objects comparable:
#interface SomeObject (Equality)
#end
#implementation SomeObject (Equality)
- (BOOL)isEqual:(SomeObject*)other
{
return self.hash == other.hash;
}
- (NSUInteger)hash
{
return self.name;///your case
}
#end
How to use:
- (NSArray*)distinctObjectsFromArray:(NSArray*)array
{
return [array valueForKeyPath:#"#distinctUnionOfObjects.self"];
}