Sort array with a count value - iphone

I have an array which contains some strings. For each character of a string an integer value is assigned. For example a=2,b=5,c=6 ,o=1,k=3 etc
The final value in the a string is the sum of the character's value. So that for an example string "BOOK" the string will be stored as "BOOK (7)". Similarly every string will have a final integer value. I would like to sort these array with these final integer values stored in the string which is present in each array index. The array contains more than 200,000 words. So the sorting process should be pretty fast. Is there any method for it?

A brutal quick example could be, if your strings structure is always the same, like "Book (7)" you can operate on the string by finding the number between the "()" and then you can use a dictionary to store temporally the objects:
NSMutableArray *arr=[NSMutableArray arrayWithObjects:#"Book (99)",#"Pencil (66)",#"Trash (04)", nil];
NSLog(#"%#",arr);
NSMutableDictionary *dict=[NSMutableDictionary dictionary];
//Find the numbers and store each element in the dictionary
for (int i =0;i<arr.count;i++) {
NSString *s=[arr objectAtIndex:i];
int start=[s rangeOfString:#"("].location;
NSString *sub1=[s substringFromIndex:start];
NSString *temp1=[sub1 stringByReplacingOccurrencesOfString:#"(" withString:#""];
NSString *newIndex=[temp1 stringByReplacingOccurrencesOfString:#")" withString:#""];
//NSLog(#"%d",[newIndex intValue]);
[dict setValue:s forKey:newIndex];
}
//Sorting the keys and create the new array
NSArray *sortedValues = [[dict allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSMutableArray *newArray=[[NSMutableArray alloc]init];
for(NSString *valor in sortedValues){
[newArray addObject:[dict valueForKey:valor]];
}
NSLog(#"%#",newArray);
This prints:
(
"Book (99)",
"Pencil (66)",
"Trash (04)"
)
(
"Trash (04)",
"Pencil (66)",
"Book (99)"
)

as i understand, you want to sort an array which contains string formated in the following
a=3
and you want to sort according to the number while ignoring the character.
in this case the following code will work with you
-(NSArray *)Sort:(NSArray*)myArray
{
return [myArray sortedArrayUsingComparator:(NSComparator)^(id obj1, id obj2)
{
NSString *first = [[obj1 componentsSeparatedByString:#"="] objectAtIndex:1];
NSString *second = [[obj2 componentsSeparatedByString:#"="] objectAtIndex:1];
return [first caseInsensitiveCompare:second];
}];
}
How to use it:
NSArray *arr= [[NSArray alloc] initWithObjects:#"a=3",#"b=1",#"c=4",#"f=2", nil];
NSArray *sorted = [self Sort:arr];
for (NSString* str in sorted)
{
NSLog(#"%#",str);
}
Output
b=1
f=2
a=3
c=4

Try this methods
+(NSString*)strTotalCount:(NSString*)str
{
NSInteger totalCount = 0;
// initial your character-count directory
NSDictionary* characterDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:2], [NSString stringWithUTF8String:"a"],
[NSNumber numberWithInt:5], [NSString stringWithUTF8String:"b"],
[NSNumber numberWithInt:6], [NSString stringWithUTF8String:"c"],
[NSNumber numberWithInt:1], [NSString stringWithUTF8String:"o"],
[NSNumber numberWithInt:3], [NSString stringWithUTF8String:"k"],
nil];
NSString* tempString = str;
for (NSInteger i =0; i<tempString.length; i++) {
NSString* character = [tempString substringWithRange:NSMakeRange(i, 1)];
character = [character lowercaseString];
NSNumber* count = [characterDictionary objectForKey:character];
totalCount += [count integerValue];
};
return [NSString stringWithFormat:#"%#(%d)",str,totalCount];
}
The test sentence:
NSLog(#"%#", [ViewController strTotalCount:#"BOOK"]);
will output " BOOK(10) "
You may change the ViewController to you own class name;

First - create a custom object to save your values. Don't put the value inside the string.
Sorting is not your base problem. The problem is that you are saving values into a string from where they are difficult to extract.
#interface StringWithValue
#property (nonatomic, copy, readwrite) NSString* text;
#property (nonatomic, assign, readwrite) NSUInteger value;
- (id)initWithText:(NSString*)text;
- (NSComparisonResult)compare:(StringWithValue*)anotherString;
#end
#implementation StringWithValue
#synthesize text = _text;
#synthesize value = _value;
- (id)initWithText:(NSString*)text {
self = [super init];
if (!self) {
return nil;
}
self.text = text;
self.value = [self calculateValueForText:text];
return self;
}
- (NSComparisonResult)compare:(StringWithValue*)anotherString {
if (self.value anotherString.value) {
return NSOrderedDescending;
}
else {
return NSOrderedSame;
}
}
- (NSString*)description {
return [NSString stringWithFormat:#"%# (%u)", self.text, self.value];
}
#end
Sorting the array then would be a simple use of sortUsingSelector:.
Note this will beat all other answers in performance as there is no need to parse the value with every comparison.

Related

How to swap `NSMutableDictionary` key and values in place?

I have a NSMutableDictionary and I want to swap values & keys. i.e, after swapping values becomes keys and its corresponding keys with become values All keys and values are unique. Looking for an in place solution because size is very big . Also, the keys and values are NSString objects
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:#{
#"key1" : #"value1",
#"key2" : #"value2"}];
for (NSString *key in [d allKeys]) {
d[d[key]] = key;
[d removeObjectForKey:key];
}
NSLog(#"%#", d); // => { value1 : key1,
// value2 : key2 }
Assumptions
unique values (as they will become keys)
values conform to NSCopying (same as above)
no value is equal to any key (otherwise colliding names will be lost in the process)
Here is another way to invert dictionary. The simplest for me.
NSArray *keys = dictionary.allKeys;
NSArray *values = [dictionary objectsForKeys:keys notFoundMarker:[NSNull null]];
[dictionary removeAllObjects]; // In case of huge data sets release the contents.
NSDictionary *invertedDictionary = [NSDictionary dictionaryWithObjects:keys forKeys:values];
[dictionary setDictionary:invertedDictionary]; // In case you want to use the original dictionary.
EDIT: I had written a few lines of codes to get the OP started into the task of creating his own algorithm. The answer was not well received so I have crafted a full implementation of an algorithm that does what he asks, and goes one step further.
Advantages:
Makes no assumptions regarding the contents of the dictionary, for example, the values need not conform to the 'NSCopying' protocol
Transverses the whole hierarchy of a collection, swapping all the keys
It's fast since it uses recursion and fast enumeration
Does not alter the contents of the original dictionary, it creates a brand new one
Code has been implemented through categories to both collections:
#interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue;
#end
#interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue
{
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithCapacity:self.count];
[self enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
id newKey = nil;
if ([value isKindOfClass:[NSDictionary class]]) {
newKey = [value dictionaryBySwappingKeyWithValue];
} else if ([value isKindOfClass:[NSArray class]]) {
newKey = [value arrayBySwappingKeyWithValue];
} else {
newKey = value;
}
if (![newKey conformsToProtocol:#protocol(NSCopying)]) {
newKey = [NSValue valueWithNonretainedObject:newKey];
}
mutableDictionary[newKey] = key;
}];
return [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
#end
and...
#interface NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue;
#end
#implementation NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue
{
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:self.count];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *newDict = [obj dictionaryBySwappingKeyWithValue];
mutableArray[idx] = newDict;
} else if ([obj isKindOfClass:[NSArray class]]) {
NSArray *newArray = [obj arrayBySwappingKeyWithValue];
mutableArray[idx] = newArray;
} else {
mutableArray[idx] = obj;
}
}];
return [NSArray arrayWithArray:mutableArray];
}
#end
As an example, assume you have a dictionary with the following structure:
UIView *view = [[UIView alloc] init];
NSDictionary *dict = #{#"1" : #"a",
#"2" : #[ #{ #"5" : #"b" } ],
#"3" : #{#"6" : #"c"},
#"7" : view};
NSDictionary *newDict = [dict dictionaryBySwappingKeyWithValue];
Printing the newDict object in the console will give you this output:
(lldb) po mutableDictionary
{
a = 1;
({b = 5;}) = 2;
{c = 6;} = 3;
"<30b50617>" = 7;
}
As you can see, not only have the keys and values been swapped at the first level of the hierarchy, but deep inside each collection.
"<30b50617>" represents the UIView object wrapped inside a NSValue. Since UIView does not comply to the NSCopying protocol, it needs to be handled this way if you want it to be a key in your collection.
Note: Code was done in a couple of minutes. Let me know if I missed something.
for (NSString *key in [myDictionary allKeys]) {
NSString *value = [responseDataDic objectForKey:key];
[myDictionary removeObjectForKey:key];
[myDictionary addObject:key forKey:value];
}
Assumption:
No key = value;
Complexity:
No extra space required. Will loop through once and replace all key value pairs.
NSArray* allKeys = [theDict allKeys];
NSArray* allValues = [theDict allValues];
NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithObjects:allKeys forKeys:allValues];

compare Integer values using array

I have some integer values inside NSMutableArray. I have added a UITextField and a UIButton. If a number is entered inside textfield and clicking on the button does a comparison. If number entered matches, I need to show NSLog. But it's not working.
code:
arr = [[NSMutableArray alloc]init];
[arr addObject:[NSNumber numberWithInteger:1]];
Button click:
-(void)click:(id)sender{
if (text.text == [arr objectAtIndex:0]){
NSLog(#"values matched");
}
}
Try this
-(void)click:(id)sender{
NSString *str = [NSString alloc]initWithFormat:#"%d",[arr objectAtIndex:0]];
if([text.text isEqualToString: str]){
NSLog(#"values matched");
}
}
I am assuming the array contains NSNumber objects; and if so convert the textfield content to an NSNumber object and use [NSArray indexOfObject] to find it in the array:
- (void)click:(id)sender{
NSNumber *num = [NSNumber numberWithInt:[text.text intValue]];
NSUInteger index = [arr indexOfObject:num];
if (index != NSNotFound) {
NSLog(#"values matched");
}
}

How to search in NSArray?

I am having an array like fallowing,
NSArray*array = [NSArray arrayWithObjects:#"1.1 something", #"1.2 something else", #"1.3 out of left field", #"1.4 yet another!", nil];
Now,i am having the string like fallowing,
NSString*str = #"1.3";
Now i will send the str .Then it needs to find that str in array and it need to return the index of object where that text found.Means i need index 2 has to come as output.Can anyone share the code please.Thanks in advance.
Here is an example using blocks, notice the method: hasPrefix:
NSArray *array = [NSArray arrayWithObjects:#"1.1 problem1", #"1.2 problem2", #"1.3 problem3", #"1.4 problem4", nil];
NSString *str = #"1.3";
NSUInteger index = [array indexOfObjectPassingTest:
^(id obj, NSUInteger idx, BOOL *stop) {
return [obj hasPrefix:str];
}];
NSLog(#"index: %lu", index);
NSLog output:
index: 2
First a comment,
NSString *str = 1.3;
does not create an NSString object. You should instead have
NSString *str = #"1.3";
To search the NSArray, you will either have to change the string to the exact string in the array or search the NSString as well. For the former, simply do
float num = 1.3;
NSString *str = [NSString stringWithFormat:#"%.1f problem%d",num,(num*10)%10];
[array indexOfObject:str];
You can get fancier using NSPredicates as well.
Try
NSString *searchString = [str stringByAppendingFormat: #" problem%#", [str substringFromIndex: 2]];
NSUInteger index = [array indexOfObject: searchString];
Or (because you somehow like oneliners):
[array indexOfObject: [[array filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: #"SELF beginswith %#", str]] objectAtIndex: 0]];
The simplest way is to enumerate through values of array and check substrings:
NSArray *array = [NSArray arrayWithObjects: #"1.1 something", #"1.2 something else", #"1.3 out of left field", #"1.4 yet another!", nil];
NSString *str = #"1.33";
int i = -1;
int index = -1;
for (NSString *arrayString in array) {
i++;
if ([arrayString rangeOfString: str].location != NSNotFound) {
index = i;
break;
}
}
NSLog(#"Index: %d", index);
Not optimal but will work.

Build string with variable number of keys and formats

I have an NSDictionary object that contains my data. I am passing in an array of key names and a display format for a string representation of my data.
[self displayMyDataWithTheseKeys:myKeyArray inThisFormat:myFormat];
where, for example,
myKeyArray = [NSArray arrayWithObjects: #"Key1", #"Key2", nil];
myFormat = [NSString stringWithString: #"%# to the %# degree"];
However, myFormat may change and the number of keys in the array may vary as well.
If the number of elements in the array was always 2, this would be trivial. However, how can I handle a variable number of elements?
There isn't really a built-in method for this, but it's relatively easy to parse format strings with NSScanner. Here's a simple example, it only handles %# format specifiers, but as all elements in an NSArray are objects and not primitive types anyway, it shouldn't matter:
NSArray *myKeyArray = [NSArray arrayWithObjects: #"Key1", #"Key2", nil];
NSString *myFormat = [NSString stringWithString: #"%# to the %# degree"];
NSMutableString *result = [NSMutableString string];
NSScanner *scanner = [NSScanner scannerWithString:myFormat];
[scanner setCharactersToBeSkipped:[NSCharacterSet illegalCharacterSet]];
int i = 0;
while (![scanner isAtEnd]) {
BOOL scanned = [scanner scanString:#"%#" intoString:NULL];
if (scanned) {
if (i < [myKeyArray count]) {
[result appendString:[myKeyArray objectAtIndex:i]];
i++;
} else {
//Handle error: Number of format specifiers doesn't
//match number of keys in array...
}
}
NSString *chunk = nil;
[scanner scanUpToString:#"%#" intoString:&chunk];
if (chunk) {
[result appendString:chunk];
}
}
Use: stringByAppendingString
Here's an example on how to use it:
NSString *someString = #"String";
someString = [someString stringByAppendingString:[NSString stringWithFormat:#"%#",variable1]];
someString = [someString stringByAppendingString:[NSString stringWithFormat:#"%#",variable2]];
someString = [someString stringByAppendingString:[NSString stringWithFormat:#"%#",variable3]];
...and so on
If you have an array of keys which you want to put in a string:
NSString *string = #"And the keys are:\n";
for(int i = 0; i < [array count]; i++)
{
NSString *thisKey = (NSString *)[array objectAtIndex:i];
string = [string stringByAppendingString:[NSString stringWithFormat:#"Key number %d is %#",i,thisKey]];
}

NSMutableArray of Objects

First off I am very new to Objective C and iPhone programming. Now that that is out of the way. I have read through most of the Apple documentation on this and some third party manuals.
I guess I just want to know if I'm going about this the correct way ...
- (NSMutableArray *)makeModel {
NSString *api = #"http://www.mycoolnewssite.com/api/v1";
NSArray *namesArray = [NSArray arrayWithObjects:#"News", #"Sports", #"Entertainment", #"Business", #"Features", nil];
NSArray *urlsArray = [NSArray arrayWithObjects:
[NSString stringWithFormat:#"%#/news/news/25/stories.json", api],
[NSString stringWithFormat:#"%#/news/sports/25/stories.json", api],
[NSString stringWithFormat:#"%#/news/entertainment/25/stories.json", api],
[NSString stringWithFormat:#"%#/news/business/25/stories.json", api],
[NSString stringWithFormat:#"%#/news/features/25/stories.json", api], nil];
NSMutableArray *result = [NSMutableArray array];
for (int i = 0; i < [namesArray count]; i++) {
NSMutableDictionary *objectDict = [NSMutableDictionary dictionary];
NSString *name = (NSString *)[namesArray objectAtIndex:i];
NSString *url = (NSString *)[urlsArray objectAtIndex:i];
[objectDict setObject:name forKey:#"NAME"];
[objectDict setObject:url forKey:#"URL"];
[objectDict setObject:#"NO" forKey:#"HASSTORIES"];
[result addObject:objectDict];
}
return result;
}
The output of the result is ...
(
{
HASSTORIES = NO;
NAME = News;
URL = "http://www.mycoolnewssite.com/api/v1/news/news/25/stories.json";
},
{
HASSTORIES = NO;
NAME = Sports;
URL = "http://www.mycoolnewssite.com/api/v1/news/sports/25/stories.json";
},
{
HASSTORIES = NO;
NAME = Entertainment;
URL = "http://www.mycoolnewssite.com/api/v1/news/entertainment/25/stories.json";
},
{
HASSTORIES = NO;
NAME = Business;
URL = "http://www.mycoolnewssite.com/api/v1/news/business/25/stories.json";
},
{
HASSTORIES = NO;
NAME = Features;
URL = "http://www.mycoolnewssite.com/api/v1/news/features/25/stories.json";
}
)
Any insight would be appreciated ;-)
It looks fine. There can be some minor improvements if you care.
1.
[NSString stringWithFormat:#"%#/news/news/25/stories.json", api]
can be replaced by
[api stringByAppendingString:#"/news/news/25/stories.json"]
if there's no chance the api appears in the middle or accepts other arguments.
2.
NSString *name = (NSString *)[namesArray objectAtIndex:i];
NSString *url = (NSString *)[urlsArray objectAtIndex:i];
The explicit cast is unnecessary. An id can be implicitly casted to and from other ObjC objects.
3.
You could use a convenient method -dictionaryWithObjectsAndKeys: to construct the dictionary in one-shot, so you don't need a temperary dictionary:
[result addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
name, #"NAME",
url, #"URL",
#"NO", #"HASSTORIES", nil]];
4. (optional)
This transform is not useful if the function is not a hot spot.
Since the arrays are only used locally, it's more efficient to use a C array.
static const int arraySize = 5;
NSString* namesCArray[] = {#"News", #"Sports", #"Entertainment", #"Business", #"Features"};
NSString* urlsCArray[arraySize];
urlsArray[0] = [api stringByAppendingString:#"/news/news/25/stories.json"];
...
for (int i = 0; i < arraySize; ++ i) {
...
NSString* name = namesCArray[i];
NSString* url = urlsCArray[i];
...
}
this removes the repeated -count and -objectAtIndex: calls which is very slow compared with direct element access.
5. (optional)
This transform is not useful if the array is short.
You could use fast-enumeration to loop over an ObjC container:
int i = 0;
for (NSString* name in namesArray) {
NSString* url = [urlsArray objectAtIndex:i];
...
++ i;
}
6.
Usually we use [NSNumber numberWithBool:NO] to represent a boxed true/false value, instead of a string #"NO". NSNumber is also used a lot whenever a primitive number (int, float, etc.) cannot be used (e.g. to be stored in an NSArray). I don't know if your API explicitly requires a string NO, so it may not unsuitable for you.