NSMutableDictionary don't set value for key - objective-c

I have a code in which the existing handle dates in an array, separates the day of the month and will put inside a NSMutableDictionary with their respective day of the month, the array has the following structure:
"12/01/2014" //Structure of my date is -> dd-mm-yyyy
"16/01/2014"
"30/01/2014"
"02/02/2014"
"08/02/2014"
I use this code to put this values inside a NSMutableDictionary:
dictionary = [NSMutableDictionary dictionary];
for(int x=0;x<[list count];x++){
NSMutableArray *lstaInfo2 = [[list[x] componentsSeparatedByString:#"/"] mutableCopy];
if([lstaInfo2[1] isEqual: #"01"]){
[dictionary setValue:list[x] forKey:[NSString stringWithFormat:#"January of %#",lstaInfo2[2]]];
}
if([lstaInfo2[1] isEqual: #"02"]){
[dictionary setValue:list[x] forKey:[NSString stringWithFormat:#"February of %#",lstaInfo2[2]]];
}
}
The values ​​that I hoped to return within the variable dictionary is:
January of 2014 =>
"12/01/2014"
"16/01/2014"
"30/01/2014"
February of 2014 =>
"02/02/2014"
"08/02/2014"
But the variable dictionary, returns only the last values, like this:
January of 2014 =>
"30/01/2014"
February of 2014 =>
"08/02/2014"
why? How can I solve this problem?

dictionary = [NSMutableDictionary dictionary];
for(int x=0;x<[list count];x++){
NSString* key = [self getKeyForDate:list[x]];
NSMutableArray* listForMonth = dictionary[key];
if (key == nil) {
listForMonth = [NSMutableArray array];
[dictionary setValue:listForMonth forKey:key];
}
[listForMonth addObject:list[x]];
}
.....
In init
monthArray = #[#"January", #"February", #"March" ...
Separate method:
-(NSString*) getKeyForDate:(NSString*)date {
NSMutableArray *lstaInfo2 = [date componentsSeparatedByString:#"/"] mutableCopy];
NSInteger monthNum = lstaInfo2[0].integerValue;
NSString* result = [NSString stringWithFormat;#"%# of %#", monthArray[monthNum-1], lstaInfo2[2]];
return result;
}
You can also use NSDateFormatter to parse the date, and NSCalendar to serve up the month names, but that's getting too deep for one lesson.

When you try to add new value to the dictionary check whether value for this key is actually in the dictionary. If no, create and set NSMutableArray object for this key and add values to this array.
Try this:
```objc
dictionary = [NSMutableDictionary dictionary];
for(int x=0; x < [list count]; x++){
NSArray *lstaInfo2 = [list[x] componentsSeparatedByString:#"/"];
NSString *key;
if([lstaInfo2[1] isEqual: #"01"]){
key = [NSString stringWithFormat:#"January of %#",lstaInfo2[2]];
}
if([lstaInfo2[1] isEqual: #"02"]){
key = [NSString stringWithFormat:#"February of %#",lstaInfo2[2]];
}
if (key) {
//if there is no value for the specified key create and set
//NSMutableArray object for this key, otherwise keep value for
//the key without modyfing it.
dictionary[key] = dictionary[key] ?: [NSMutableArray array];
[dictionary[key] addObject:list[x]];
}
}
```

It is because setValue:forKey: doesn't append to the current value. So in the first iteration through the loop you set "January of 2014" => ["12", "01", "2014"] and in the next iteration you set "January of 2014" => ["16", "01", "2014"], and so on. You want to map from a string to a NSMutableArray.
I would really suggest you use the NSDateFormatter class for this: https://developer.apple.com/library/ios/documentation/cocoa/reference/foundation/classes/nsdateformatter_class/Reference/Reference.html#//apple_ref/occ/instm/NSDateFormatter/. It will be easier and much more flexible than manually parsing the dates.

Related

NSString parsing for specific values

I have an NSString that returns a list of values like this:
test_1=value_1
test/2=value_2
test3=value_3 value_4
test_4=value_5/value_6
...
More realistic result values:
inameX=vlan2
hname=server
lanipaddr=192.168.1.1
lannetmask=255.255.255.0
islan=0
islwan=0
dhcplease=604800
dhcplease_1=302400
ct_tcp_timeout=0 1200 40 30 60 60 5 30 15 0
ct_timeout=10 10
ct_udp_timeout=25 60
ctf_disable=1
ddnsx0=
cifs2=0<\\192.168.1.5
and so on...
If I do:
for (id key in dict) {
NSLog(#"key: %#, value: %#", [dict objectForKey:key], key);
}
it outputs:
key: inameX, value: vlan2
key: hname value: server
key: lanipaddr value: 192.168.1.1
key: lannetmask value: 255.255.255.0
This list is stored in one NSString *result. Not sure if I should put it in an array for this but I need to be able to call a function or command that will return a specific value_X based on the argument to match the variable. For example, get value of test_1 variable then it would return value_1. Or get test_4 then it would return value_5/value_6
Any idea how I can do that?
I appreciate your help. Thanks!
You probably want the method in NSString called componentsSeparatedByCharactersInSet: to split up that one string into an array. Since your values are separated by '=' and new line characters ('\n'), you want the set to include those two characters:
NSArray *strings = [NSString componentsSeparatedByCharactersInSet:
[NSCharacterSet characterSetWithCharactersInString:#"=\n"]];
And then you can make this into a dictionary with NSDictoinary's dictionaryWithObjects: AndKeys: But first, you need to split that array into two arrays; one with objects, one with keys:
NSMutableArray *keys = [NSMutableArray new];
NSMutableArray *values = [NSMutableArray new];
for (int i = 0; i < strings.count; i++) {
if (i % 2 == 0) { // if i is even
[keys addObject:strings[i]];
}
else {
[values addObject:strings[i]];
}
}
Then you put them into an NSDictonary
NSDictionary *dict = [NSDictionary dictionaryWithObjects:values forKeys:keys];
NSLog(#"%#", dict[#"test_1"]) // This should print out 'value_1'
Hope that helps!
Use an NSDicationary. NSDictionaries are key value stores. In other words, there are a list of keys. Each key is unique. Each key has an associated value. The value can be any data type and the key has to conform to the NSCopying protocol (typically an NSString). If you try to access the value for a key that doesn't exist in your NSDictionary, the return value will be nil.
//create the dictionary and populate it
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:#"value_1" forKey:#"key_1"];
[dict setObject:#"value_2" forKey:#"key_2"];
[dict setObject:#"value_3" forKey:#"key_3"];
[dict setObject:#"value_4" forKey:#"key_4"];
NSString *stringInput = [self getStringInput];//however you find out your input
//find your string value based on the key passed in
NSString *strValue = [dict objectForKey:stringInput];
You can use a NSScanner to make this work.
Scan for the string for which you want the value, and then scan the string until you encounter \n and then use it for your requirement.
NSScanner *scan =[NSScanner scannerWithString:theString];
[scan scanString:keyString inToString:nil];
[scan setScanLocation:[scan scanLocation]+1];
[scan scanString:#"\n" inToString:requiredString];
So requiredString is the string which you want.

How to loop through and add duplicates in an Array

I have a Dictionary with an Orders array with amount and orders in it Orders = ("3 White Shirts", "8 White Shirts", "4 blue shorts")
How would I loop through it and add the amount of duplicate orders so that the result string or array would be
Orders = ("11 White Shirts", "4 blue shorts") or myString ="11 White Shirts, 4 blue shorts"
I'm thinking some sort of substring to check if the products are the same but not sure how to capture the correct quantity to add from the duplicate order
Many thanks
Ok here is a way to do this (the shortest one I could think of):
// Assuming that 'orders' is the array in your example
NSMutableDictionary *orderDict = [[NSMutableDictionary alloc] init];
for (NSString *order in orders)
{
// Separate the string into components
NSMutableArray *components = [[order componentsSeparatedByString:#" "] mutableCopy];
// Quantity is always the first component
uint quantity = [[components objectAtIndex:0] intValue];
[components removeObjectAtIndex:0];
// The rest of them (rejoined by a space is the actual product)
NSString *item = [components componentsJoinedByString:#" "];
// If I haven't got the order then add it to the dict
// else get the old value, add the new one and put it back to dict
if (![orderDict valueForKey:item])
[orderDict setValue:[NSNumber numberWithInt:quantity] forKey:item];
else{
uint oldQuantity = [[orderDict valueForKey:item] intValue];
[orderDict setValue:[NSNumber numberWithInt:(oldQuantity+quantity)] forKey:item];
}
}
This would give you a dict like this:
{
"White Shirts" = 11;
"blue shorts" = 4;
}
So you could iterate over the keys and produce an array of strings like this:
NSMutableArray *results = [[NSMutableArray alloc] initWithCapacity:0];
for (NSString *key in [orderDict allKeys])
{
[results addObject:[NSString stringWithFormat:#"%# %#", [orderDict valueForKey:key], key]];
}
Which finally will give you:
(
"11 White Shirts",
"4 blue shorts"
)
PS. Don't forget to release if you don't use ARC !
You need to parse the strings to extract the two pieces of information: order quantity, which is a numeric value, and item identification, which may remain a string.
Use a NSMutableDictionary mapping the item identification to a numeric value representing the current order quantity, else retrieve the old total and add to it the current order, then update the dictionary.
At the end iterate the dictionary and transform each key-value pair back into a string.
Since it looks like your array contains string objects, I would do something like this:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSArray *ordersAsStrings = [NSArray arrayWithObjects:#"7 white shirts", #"4 blue jeans", #"3 white shirts", #"4 blue jeans", nil];
NSMutableDictionary *combinedQuantities = [NSMutableDictionary new];
NSMutableArray *combinedOrdersAsStrings = [NSMutableArray new];
// take each string and break it up into the quantity and the item
for (NSString *orderAsString in ordersAsStrings) {
NSInteger scannedQuantity = 0;
NSString *scannedItem = nil;
NSScanner *scanner = [NSScanner scannerWithString:orderAsString];
[scanner scanInteger:&scannedQuantity];
[scanner scanCharactersFromSet:[[NSCharacterSet illegalCharacterSet] invertedSet] intoString:&scannedItem];
// if the item is already in combinedOrders
if ([combinedQuantities.allKeys containsObject:scannedItem] == YES) {
// update quantity
NSNumber *existingQuantity = [combinedQuantities objectForKey:scannedItem];
NSInteger combinedQuantity = existingQuantity.integerValue + existingQuantity.integerValue;
[combinedQuantities setObject:[NSNumber numberWithInteger:combinedQuantity] forKey:scannedItem];
} else {
// otherwise add item
NSNumber *quantity = [NSNumber numberWithInteger:scannedQuantity];
[combinedQuantities setObject:quantity forKey:scannedItem];
}
}
// change combined quantities back into strings
for (NSString *key in combinedQuantities.allKeys) {
NSNumber *quantity = [combinedQuantities objectForKey:key];
NSString *orderAsString = [NSString stringWithFormat:#"%ld %#", quantity.integerValue, key];
[combinedOrdersAsStrings addObject:orderAsString];
}
NSLog(#"Combined Orders: %#", combinedOrdersAsStrings);
}
return 0;
}

splitting nsstring and putting into tableview

I am trying to split the string into parts and insert into a table how should i do it?
I got an error for splitting of the array which is: -[__NSArrayI componentsSeparatedByString:]: unrecognized selector sent to instance 0x7a421e0
NSArray *BusRoute = alightDesc;
int i;
int count = [BusRoute count];
for (i = 0; i < count; i++)
{
NSDictionary *dic = [BusRoute objectAtIndex: i];
NSDictionary *STEPS = [dic valueForKey:#"STEPS"];
NSString *AlightDesc = [STEPS valueForKey:#"AlightDesc"];
NSLog(#"AlightDesc = %#", AlightDesc);
NSArray *aDescArray = [AlightDesc componentsSeparatedByString:#","];
NSLog(#"aDescArray = %#", aDescArray);
}
This is the string which I'm splitting, i got it from the NSLog
AlightDesc = (
"Block1",
"Block2",
"Block3"
)
please help I'm stuck thanks.
Objective C is not a strongly typed language. All you know for sure about [STEPS valueForKey:#"AlightDesc"] is that it will return an object (of type id). When you wrote NSString *AlightDesc = [STEPS valueForKey:#"AlightDesc"] the compiler did not complain because NSString * is a valid object type. Unfortunately there is a logic error in your code so that what was actually stored under the key #"AlightDesc" is an NSArray. As others have mentioned, NSArray does not respond to componentsSeparatedByString: so you get an error at runtime.
The easy fix for this is to correct your logic: Either store an NSString in the first place or treat what you get out as an NSArray. As #janusfidel mentioned you can use an NSArray perfectly well in a table by using objectAtIndex: to get the string for the entry you want.
In some more complicated cases you may not know what you will be getting out of a dictionary for a particular key. In that case in Objective C you can just ask the object:
id anObject = [STEPS valueForKey:#"AlightDesc"];
if ([anObject isKindOfClass:[NSString class]]) {
NSString *aString = (NSString *)anObject;
// Treat as a string ...
} else if ([anObject isKindOfClass:[NSString class]]) {
// Object is an array ...
Your NSString *AlightDesc should look like this
NSString *AlightDesc = "Block1,Block2,Block3";
NSArray *aDescArray = [AlightDesc componentsSeparatedByString:#","];
If your string is what you say it is
AlightDesc = ("Block1","Block2","Block3");
then your string is the problem because it's already broken up.

all Keys and Objects from a NSDictionary to a NSString

I've got a NSDictionary and I'd like put all of it's objects and keys into a NSString, so that I can finally display them in a label like this:
key1: object1
key2: object2
key3: object3
... ...
Any ideas?
Build the string and then set it to the labels text.
NSMutableString *myString = [[NSMutableString alloc] init];
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[myString appendFormat:#"%# : %#\n", key, obj];
}];
self.label.text = myString;
Note
The docs (String Programming Guide) for the %# format specifier state:
%#
Objective-C object, printed as the string returned by descriptionWithLocale: if available, or description otherwise. Also works with CFTypeRef objects, returning the result of the CFCopyDescription function.
So if these are your own custom objects in the dictionary you will most likely need to override the description method to provide more meaningful output
Update
You mention that you need your output sorted by keys - dictionaries are not ordered so you will have to do it differently - this example assumes that your keys are strings
NSArray *sortedKeys = [[dictionary allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
NSMutableString *myString = [[NSMutableString alloc] init];
for (NSString *key in sortedKeys) {
[myString appendFormat:#"%# : %#\n", key, [dictionary objectForKey:key]];
}
self.label.text = myString;
try this
for(id key in [dictionary allKeys])
{
id value = [dictionary objectForKey:key];
NSLog(#"%# : %#", key, value);
}
NSString *row;
for (id key in dictionary) {
row = [NSString stringWithFormat:"%#: %#", key, [dictionary objectForKey:key]];
// do something with your row string
}
Iam trying to append two time values from my TCTime object and display in label, even though I added "\n" towards the end it is printing only the first value but not the second value.
_feedTimes is NSArray.
NSMutableString *myString = [[NSMutableString alloc] init];
TCTime *time = _feedTimes[indexPath.row];
[myString appendFormat:#"%s : %#\n", "Time 1 ", time.Time1];
[myString appendFormat:#"%s : %#\n", "Time 2 ", time.Time2];
self.label.text = myString;

Adding NSMutableArray to another from a loop seems to create duplicates

I'm parsing through an NSDictionary of json-encoded events and placing them into a two-dimensional NSMutableArray based on their month -- for display in a sectioned table view.
Since I am adding items to an array and then placing that array in an array (event_container) in a loop, event_container shows the correct number of arrays, however, they all appear to be duplicates of the last iteration, so all of the contents of event_container are the same array.
I believe this is because it's a pointer and/or not being released. I'm unsure of an appropriate way around this or possibly even a better solution. I'm using ARC.
int month = 0;
int current_month = 0;
int counter = 0;
event_container = [[NSMutableArray alloc] init];
temp_array = [[NSMutableArray alloc] init];
for (NSDictionary *result in results)
{
NCEvent *anEvent = [[NCEvent alloc] init];
anEvent.title = [result objectForKey:#"title"];
anEvent.startdate = [result objectForKey:#"startdate"];
anEvent.enddate = [result objectForKey:#"enddate"];
NSDateFormatter *importDate = [[NSDateFormatter alloc] init];
[importDate setDateFormat:#"yyyy-M-d H:m:ss"];
anEvent.dateStart = [importDate dateFromString:anEvent.startdate];
anEvent.dateEnd = [importDate dateFromString: anEvent.enddate];
NSDateFormatter *exportDate = [[NSDateFormatter alloc] init];
[exportDate setDateFormat:#"d"];
anEvent.text_date = [exportDate stringFromDate: anEvent.dateStart];
NSDateFormatter *exportMon = [[NSDateFormatter alloc] init];
[exportMon setDateFormat:#"MMM"];
anEvent.text_mon = [exportMon stringFromDate: anEvent.dateStart];
NSDateFormatter *monthInt = [[NSDateFormatter alloc] init];
[monthInt setDateFormat:#"M"];
month = [[monthInt stringFromDate: anEvent.dateStart] intValue];
if(counter == 1){ //first month
current_month = month;
NSLog(#"I'm the first month: %i", month);
[temp_array addObject:anEvent];
}
else if(month > current_month){ //new month
NSLog(#"This is a new month");
current_month = month;
//add the events array to events container and reset the events array
[self.event_container addObject: temp_array];
[temp_array removeAllObjects];
[temp_array addObject:anEvent];
}
else{
NSLog(#"Same Month"); //same month
[temp_array addObject:anEvent];
}
NSLog(#"Event month integer: %i", month);
anEvent = nil;
counter++;
}
Those arrays are declared as properties:
#property (nonatomic, retain) NSMutableArray *event_container;
#property (nonatomic, retain) NSMutableArray *temp_array;
In the line:
[self.event_container addObject: temp_array];
You are always adding the same instance temp_array to self.event_container. This is why you see the same array duplicated many times.
You can solve this by doing the following for example:
-Add the following before your for loop
for (int i = 0; i < 12; i++) {
[event_container addObject:[NSMutableArray array]];
}
for (NSDictionary *result in results)
...
-Remove
if(counter == 1){ //first month
current_month = month;
NSLog(#"I'm the first month: %i", month);
[temp_array addObject:anEvent];
}
-and change the code that comes after that into :
tmp_array = [event_container objectAtIndex:month];
[temp_array addObject:anEvent];
Your suspicions about the array being a pointer is basically correct. The problem is that your temp_array isn't so temporary -- it's in fact the same array object every time through your loop.
You're creating it outside the loop, and whenever you send it addObject: or removeAllObjects, it's affecting the stuff that you've already put in there.
The key part, though, is that when you add the temp_array to event_container, it's the exact same object. It's not copied; the event_container array just gets a pointer to temp_array. When you add it again, it's the same thing. Since event_container just holds a whole bunch of pointers, you end up looking at the same object when you inspect it.
That's what's happening. To solve this, you need to create a separate array for each month; I think that sch's answer will work for you.
A quick demonstration:
NSMutableArray * container = [NSMutableArray array];
NSMutableArray * temp = [NSMutableArray array];
int i;
for( i = 0; i < 5; i++ ){
[temp addObject:[NSNumber numberWithInt:i]];
[container addObject:temp]; // Doesn't copy; just adds pointer to temp
[temp removeAllObjects];
}
// Inspecting container now, we find that it has five arrays, all empty.
NSLog(#"%#", container);
temp_array is a pointer type (like all objects in objective c). Therefore, with this call:
[self.event_container addObject: temp_array];
...you are adding a pointer to that object to event_container. You are not creating a new array, merely adding multiple pointers to the same object. What you most likely want to do is add a (pointer to a) copy of the object, like this:
[self.event_container addObject: [temp_array mutableCopy]];