Sorting of NSMutableDictionary - iphone

Confusing !!!
I have one NSMutableDictionary called tempDict, having keys Freight, Fuel , Discount (and many more) with relevant values.
I am generating two different NSMutableArrays called arrTVBuyCharge and arrTVBuyCost from tempDict using this Code :
[arrTVBuyCharge addObjectsFromArray:[(NSArray *)[tempDict allKeys]]];
[arrTVBuyCost addObjectsFromArray:[(NSArray *)[tempDict allValues]]];
Problem : I want Freight, Fuel and Discount at the Top in the above arrays in same order (Ofcourse , with Ordering of Values).
What is the Optimum way to achieve this ?

It seems tricky at first, but it's simple when you think about it. All you want to do is get a sorted list of keys, and look up the value for each key as you add them to your arrays.
To get an array with the list of sorted keys:
NSArray *sortedKeys = [[tempDict allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
Then iterate through those and add them to NSMutableArrays:
NSMutableArray *arrTVBuyCharge = [[NSMutableArray alloc] init];
NSMutableArray *arrTVBuyCost = [[NSMutableArray alloc] init];
for (NSString *key in sortedKeys) {
[arrTVBuyCharge addObject:key];
[arrTVBuyCost addObject:[tempDict objectForKey:key]];
}
For even better performance, use the initWithCapacity method for the NSMutableArrays since you know the size.
This is the standard way of doing it.

1) Get three objects separately :
NSArray *mainKeys = #[#"Freight", #"Fuel", #"Discount"];
NSArray *mainValues = #[[tempDict valueForKey:#"Freight"],
[tempDict valueForKey:#"Fuel"],
[tempDict valueForKey:#"Discount"]
];
[arrTVBuyCharge addObjectsFromArray:mainKeys];
[arrTVBuyCost addObjectsFromArray:mainValues];
2) Remove them from tempDict :
[tempDict removeObjectsForKeys:mainKeys];
3) Add the objects from Updated tempDict :
[arrTVBuyCharge addObjectsFromArray:(NSArray *)[tempDict allKeys]];
[arrTVBuyCost addObjectsFromArray:(NSArray *)[tempDict allValues]];
This will make Freight, Fuel and Discount to be at index 0, 1 and 2 in your new Arrays.

Man, NSMutableDictionary always returns keys in a disordered fashion, if you really want to maintain order then you can sort it in alphabetical order or you can add 01, 02 ,03 serial numbers before your values to sort them in the order they were put it, later trim the first two characters of the string and use it.

Related

[Xcode]How to count NSArray according to type

I would like to ask how could I count object type inside array. For example
this is my dataclass
+ (id)LocationWithID:(NSString *)UniqueID name:(NSString *)name address:(NSString *)address latitude:(float )latitude longitude:(float )longitude locationtype:(NSString *)locationtype country:(NSString *)country distance:(float )distance;
and this is my array
[array addObject:[dataclass LocationWithID:UniqueID name:name address:address latitude:latitude longitude:longitude locationtype:locationtype country:country distance:distance]];
and i would like to count how many country in this object. Normally we use
[array count];
but this will count all the object in the array. Now in my case i want to count non-repeated object for example i got an array contain 3 United kingdom, 5 USA, 7 Japan, 3 China and the result i want is country type = 4.
I would like to create several section for Table View according to the country type. Anyone?
You can use a dictionary to put objects with the same country in a separate array:
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
for (dataclass *location in array) {
if (![dictionary objectForKey:location.country]) {
[dictionary setObject:[NSMutableArray array] forKey:location.country];
}
[(NSMutableArray *)[dictionary objectForKey:location.country] addObject:location];
}
Now, not only you can get the number of different countries like this:
int countriesCount = [dictionary count];
But also, it is easy to populate each section of your table view with locations from the corresponding country.
You can retrieve all the locations for a specific country that has the name countryName, like this:
NSArray *locationsForCountry = [dictionary objectForKey:countryName];
You can also have the list of all the countries like this:
NSArray *countries = [[dictionary keyEnumerator] allObjects];
Enumerate each item in the array and for each item get the country and add it to an NSMutableSet. After enumeration, the size of the set is the number of unique countries.
There's probably a better way to do this if you're creating a table and you just want the table to have a section for each country, but I don't know it. I'd guess that it would be something like binding the appropriate properties in the table for creating sections to the right values in the array controller or on the array controller's elements.
NSArray doesn't have this functionality itself, so, as others have pointed out, it'd be easiest to keep a separate tally of counts for each country. The best way to do this would be to create an NSCountedSet object and add the countries to that as you add your data objects to the array.
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSCountedSet_Class/Reference/Reference.html
To get the count for any country, you just call [set countForObject:country]. Simple!

How do I use scanf to call an item in an array?

My problem/question is basically: how do I use scanf to call an item in an array.
and in more detail:
I want to make a simple app which gives details of houses in a street.
I want an array/dictionary or similar which contains;
house type: terraced, detached etc. (string)
house number: (int)
Rooms: number of rooms in the house (int)
floor space: in feet squared. (int)
residents: number of people living in the house. (int)
etc.
I want to ask the user to enter a house number (in console for now but later using the iPhone UI) and then receive details of that property. Sometimes I would like to give all the details, sometimes just one or two.
I’m new to programming but have been researching loads and have learnt about arrays, dictionaries and plists. It seems I could use any of those in different ways but I think the best way is to use a combination.
I’m thinking of organising it all in a plist, using NSDictionary to store the details of each house and then putting all the houses in an array.
so in the array, houseNo1, houseNo2, houseNo3 etc
then each house/item in the array would be a dictionary with the keys; house type, rooms, floor space etc
I think that if I made the house number match up with the item number in the array then when the user enters “3” it takes the number ‘3’ and pulls the information from item3 in the array, ie. houseNo3.
I’ve been trying to do it using scanf and assigning their choice to a int called userInput but I can’t work out how to get the program to then use userInput to choose which item in the array to choose.
*I haven’t forgotten arrays start at 0 by the way, I figure I can just assign a string to item0 saying “there isn’t any houses with the door number 0” or something along those lines.
I hope I’ve made that clear, I can explain more if need be. Any help would be greatly appreciated. :)
cheers
kris
ps. I understand how to put the array/dictionary/plist together, it's just the scanf to retrieve data from the array that I'm having trouble with. - just didn't want anyone writing out lines of code explaining that unnecessarily.
Hey and welcome to programming! As far as using scanf initially and switching to iPhone UI, it's really going to be quite different since scanf is a C method and in iOS you will be getting an NSString object from the user (from a UITextView or whatever you use to let them input).
To get a number from scanf from the console use something like
int inputInteger;
printf("Enter a number:");
scanf("%d",&inputInteger);
myHouse = [myArray objectAtIndex:inputInteger];
Note that the above doesn't do error checking or valid input checking (that it's actually a number that was input).
That being said to get a integer (primitive) from a NSString you can use something like int inputInteger = [myInputString intValue]; and then use inputInteger to go to that index in the NSArray like above.
As a general programming aside: you should get used to indexing an array starting at 0, if the user inputs "1" and expects that to be the first item, you can just subtract 1 from the integer you get by the above methods and then use that to index your array (so it would really be whatever is at index 0, the first value in the array)
That was exactly what I needed thank you.
I used it to make this little program, it works as I wanted but not sure if I've gone about it in the best way. If anyone has a few minutes spare and fancies giving me some feedback that would be cool!
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//create the array
NSMutableArray *housesArray = [NSMutableArray array];
//create dictionaries
NSMutableDictionary *house1Dict = [NSMutableDictionary dictionary];
NSMutableDictionary *house2Dict = [NSMutableDictionary dictionary];
NSMutableDictionary *house3Dict = [NSMutableDictionary dictionary];
//put dictionaries in the array
[housesArray insertObject: house1Dict atIndex:0];
[housesArray insertObject: house2Dict atIndex:1];
[housesArray insertObject: house3Dict atIndex:2];
//populate the dictionaries
[house1Dict setObject:#"1" forKey:#"House number"];
[house1Dict setObject:#"semi-detached" forKey:#"House type"];
[house1Dict setObject:#"5" forKey:#"rooms"];
[house1Dict setObject:#"1,525" forKey:#"floor space"];
[house1Dict setObject:#"sea" forKey:#"view"];
[house1Dict setObject:#"friendly" forKey:#"neighbours"];
[house2Dict setObject:#"2" forKey:#"House number"];
[house2Dict setObject:#"detached" forKey:#"House type"];
[house2Dict setObject:#"8" forKey:#"rooms"];
[house2Dict setObject:#"2,685" forKey:#"floor space"];
[house2Dict setObject:#"car park" forKey:#"view"];
[house2Dict setObject:#"nosy" forKey:#"neighbours"];
[house3Dict setObject:#"3" forKey:#"House number"];
[house3Dict setObject:#"detached" forKey:#"House type"];
[house3Dict setObject:#"2" forKey:#"rooms"];
[house3Dict setObject:#"585" forKey:#"floor space"];
[house3Dict setObject:#"rear" forKey:#"view"];
[house3Dict setObject:#"drunk" forKey:#"neighbours"];
//check its all there
/*NSLog(#"in the array is%#", housesArray);
NSLog(#"at index 0 is %#", [housesArray objectAtIndex:0]);
NSLog(#"at index 1 is %#", [housesArray objectAtIndex:1]);*/
int inputInteger;
id myHouse;
printf("Which house are you interested in viewing?");
scanf("%d", &inputInteger);
myHouse = [housesArray objectAtIndex:(inputInteger-1)];
NSLog(#"House number %d is %#, has %# rooms, beautiful %# views and %# neighbours.",
inputInteger,
[myHouse objectForKey:#"House type"],
[myHouse objectForKey:#"rooms"],
[myHouse objectForKey:#"view"],
[myHouse objectForKey:#"neighbours"]);
[pool drain];
return 0;
}

Objective-C, How can I produce an array / list of strings and count for each?

My aim is to produce an array, which I can use to add section headers for a UITableView. I think the easiest way to do this, is to produce a sections array.
I want to create section headers for dates, where I'll have several or no rows for each.
So in my populate data array function, I want to populate a display array. So record 1, look for the first date in my display array, create a new array item if it doesn't exist, if it does exist add 1 to the count.
So I should end up with something like this.
arrDisplay(0).description = 1/June/2001; arrDisplay(0).value = 3;
arrDisplay(1).description = 2/June/2001; arrDisplay(1).value = 0;
arrDisplay(2).description = 3/June/2001; arrDisplay(2).value = 1;
arrDisplay(3).description = 5/June/2001; arrDisplay(3).value = 6;
My question is how do I create and use such an array with values, where I can add new elements of add to the count of existing elements and search for existing elements ?
I think, if i understand you, an NSMutableDictionary would work. (as NR4TR said) but, i think the object would be the description and the key would be the count. you could check for the key and get the count in the same gesture. if the return value of objectForKey is nil, it doesn't exist.
NSMutableDictionary *tableDictionary = [[NSMutableDictionary alloc] init];
NSString *displayKey = #"1/June/2001";
NSNumber *displayCount = [tableDictionary objectForKey:displayKey];
if (displayCount != nil) {
NSNumber *incrementedCount = [[NSNumber alloc] initWithInteger:[displayCount integerValue] + 1];
[tableDictionary removeObjectForKey:displayKey];
[tableDictionary setValue:incrementedCount
forKey:displayKey];
[incrementedCount release];
}
else {
NSNumber *initialCount = [[NSNumber alloc] initWithInteger:1];
[tableDictionary setValue:initialCount
forKey:displayKey];
[initialCount release];
}
EDIT: Hopefully this isn't pedantic, but I think a couple pointers will help.
Dictionaries, Sets, and Arrays all hold objects for retrieval. The manner of holding and retrieval desired drives the decision. I think of it based on the question 'what is the nature of the information that I have when I need an object being held?'
NSDictionary and NSMutableDictionary
Hold n objects per key. (I think...I haven't had to test a limit, but i know you can get an NSSet back as a value.)
KEY is more important than INDEX. I don't think of dictionaries as ordered. they know something and you need to ask the correct question.
NSArray and NSMutableArray
hold n objects in order.
INDEX is most important bit of information. (you can ask for the index of an object but, even here, the index is the important part)
you will typically drive table views with an array because the ordered nature of the array fits.
NSSet, NSMutableSet, and NSCountedSet
A collection of objects without order.
You can change any of these into the other with something like [nsset setFromArray:myArray];
and all of these things can hold the other as objects. I think an array as your top level is the correct thinking, but beyond that, it becomes an issue of implementation
Try array of dictionaries. Each dictionary contains two objects - section title and array of section rows.
If you want to have a description AND a rowcount then you can either create a class with those two properties and generate an NSArray of objects with that class or instead of all that you can just use an NSDictionary to store key/value lookups.
I think NSCountedSet is closest to what you want. It doesn't have an intrinsic order, but you can get an array out of it by providing a sort order.

NSDictionary split into two arrays (objects and keys) and then sorted both by the objects array (or a similar solution)

I have an NSDictionary. It has keys and objects.
For the purposes of simplicity the keys are Question numbers and the objects are calculated Answer scores.
Now how I did it before was that I set the answer score as the keys and the question numbers as the objects. This way I could get an array of allKeys from the dictionary, sort it and then do something similar to:
for(NSString *string in tempArray){
NSLog(#"%#",[dictionary objectForKey:string]);
}
The (stupid - on my part) problem that I have now encountered however is that (obviously... duuhhh) the keys need to unique, and therefore when the calculated answer scores are the same, only one answer gets output!
I need a solution to this. In PHP you can multisort arrays. I was wondering if there was some similar solution in objective-c or indeed if someone had a better answer?
Any help here would be much appreciated.
Thank you.
Do you know about the allKeys, allValues and allKeysForObject method of the NSDictionary do you ?
One solution is to store the answer scores using an array of dictionaries containing only two key-value pairs. One key is the question number (or however your questions are tagged, i.e. “Q1.1”), while the other key is the actual answer score. For example:
static NSString * const QuestionKey = #"questionNumber";
static NSString * const AnswerScoreKey = #"answerScore";
NSMutableArray *allAnswers = [NSMutableArray array];
for (int i = 0; i < 20; i++)
{
// fill allAnswers array with random data
NSDictionary *answer = [NSDictionary dictionaryWithObjectsForKeys:
[NSString stringWithFormat:#"Q%d", i], QuestionKey,
[NSNumber numberWithInt:rand()], AnswerScoreKey,
nil];
[allAnswers addObject:answer];
}
// sort the allAnswers array based on score, highest first
NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:AnswerScoreKey ascending:NO];
[allAnswers sortUsingDescriptors:[NSArray arrayWithObject:sortDesc]];
for (NSDictionary *answer in allAnswers)
{
NSLog(#"Question: %#, AnswerScore: %#", [answer objectForKey:QuestionKey], [answer objectForKey:AnswerScoreKey];
}
Disclaimer:
Untested and uncompiled code. Theory only.
I am not sure what you are actually trying to achieve. Do you want to sort an array of dictionary objects based on the value of the dictionary? If that is what you want, you can define your own comparator function and can define any custom sorting behavior. Please check sortedArrayUsingSelector: method of NSArray.
Edit : I am away from Mac currently but this previous question has a number of example code that can solve your problem. Rather than using object, you can use NSDictionary, though personally I would like to use another Question object instead of dictionary in this case. This question class will contain two value, id and score and then you need to sort the array of questions based on score just like the persons are sorted based on birthday.

Objective C: Create arrays from first array based on value

I have an array of strings that are comma separated such as:
Steve Jobs,12,CA
Fake Name,21,CA
Test Name,22,CA
Bill Gates,44,WA
Bill Nye,21,OR
I have those values in an NSScanner object so that I can loop through the values and get each comma seperated value using objectAtIndex.
So, what I would like to do, is group the array items into new arrays, based on a value, in this case, State. So, from those, I need to loop through, checking which state they are in, and push those into a new array, one array per state.
CA Array:
Steve Jobs,12,CA
Fake Name,21,CA
Test Name,22,CA
WA Array:
Bill Gates,44,WA
OR Array:
Bill Nye,21,OR
So in the end, I would have 3 new arrays, one for each state. Also, if there were additional states used in the first array, those should have new arrays created also.
Any help would be appreciated!
You can use a NSMutableDictionary of NSMutableArrays - if the state encountered isn't yet in the dictionary, add a new array.
NSMutableArray* arr = [states objectForKey:state];
if (arr == nil) {
arr = [NSMutableArray array];
[states setObject:arr forKey:state];
}
Then you can insert values into the array, preferably as objects though as Dave DeLong mentions.
You shouldn't be maintaining this data as CSV. That's asking for a world of hurt if you ever need to manipulate this data programmatically (such as what you're trying to do).
You can naïvely break this data up into an array using NSArray * portions = [line componentsSeparatedByString:#","];. Then create a custom object to store each portion (for an example, see this post), and then you can manipulate those objects almost effortlessly.
Naively: (assuming array of strings called strings)
NSMutableDictionary *states = [NSMutableDictionary dictionary];
for (NSString *string in strings) {
NSString *state = [[string componentsSeparatedByString:#", "] lastObject];
NSMutableArray *values = [states objectForKey:state];
if (values == nil) {
values = [NSMutableArray array];
[states setObject:value forKey:state];
}
[values addObject:string];
}
Number of things about this -- first of all, I'm not at my computer, so there is a high chance of typos and or things that I missed. Second, you probably want to adapt the components separated by string line to handle whitespace better.