I try to store some NSMutableDictionaries into an NSMutableArray throw a for loop:
NSMutableArray *productsIdAndQuantities = [[NSMutableArray alloc]init];
NSMutableDictionary *element = [[NSMutableDictionary alloc]init];
for (id object in shoppingArray) {
[element setObject:[object valueForKey:#"proId"] forKey:#"id"];
[element setObject:[object valueForKey:#"qty"] forKey:#"quantity"];
NSLog(#"%#",element);//tested, it's different on every iteration
[productsIdAndQuantities addObject:element];
}
After the loop end, I try to debug the productsIdAndQuantities, and just came out that it contains the exact numbers of items looped above, BUT with one value.
Here is what I mean:
Inside the loop (NSLog(#"%#",element);):
{
id = 13293;
quantity = "-2";
}
{
id = 12632;
quantity = "-5";
}
After the loop (NSLog(#"%#",productsIdAndQuantities);same value!!!!!):
(
{
id = 12632;
quantity = "-5";
},
{
id = 12632;
quantity = "-5";
}
)
You are adding the same dictionary into the array; use this instead, which allocates a new dictionary each iteration:
NSMutableArray *productsIdAndQuantities = [[NSMutableArray alloc]init];
for (id object in shoppingArray) {
NSMutableDictionary *element = [[NSMutableDictionary alloc]init];
[element setObject:[object valueForKey:#"proId"] forKey:#"id"];
[element setObject:[object valueForKey:#"qty"] forKey:#"quantity"];
NSLog(#"%#",element);//tested, it's different on every iteration
[productsIdAndQuantities addObject:element];
}
NOTE: This assumes you are using ARC, else you will leak dictionaries right, left and centre...
Related
I ran into a problem and I can't find the method to get over it. basically I need to make a mutable dictionary with some values. All values are dynamic and I get them from web service or from other variables.
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:comment.text forKey:#"Comment"];
[dict setObject:name.text forKey:#"Name"];
[dict setObject:[NSString stringWithFormat:#"%d",isPublic] forKey:#"VisibleForAll"];
po dict
{
Comment = "no comment";
Name = "test";
VisibleForAll = 1;
-> carts
}
Furthermore I want to add the following tree to my dictionary but I can't figure how to do this.
I have the necessary items in 2 NSArray artID and qty but I don't know how to create the bottom part so I can add it to the dict.
Carts {
Cart {
ArticleID : 22
Quantity : 1
}
Cart {
ArticleID : 45
Quantity : 3
}
...
}
I will add it with [dict setObject:carts forKey:#"Cart"] but I don't know how to add values in such a manner that I will make my dictionary on the form I presented you.
Also, don't forget that the numbers or Carts is flexible. I will get it from a Product.count.
Thanks in advance.
If your both array artID and qty have value at the same index for create a dictionary you can try like this
NSMutableArray *carts = [[NSMutableArray alloc] init];
for(NSInteger i=0; i<products.count; i++) {
//If you have custom class `cart` than use that
Cart *cart = [[Cart alloc] init];
cart.ArticleID = [[products objectAtIndex:i] valueForKey:#"pid"];
cart.Quantity = [[products objectAtIndex:i] valueForKey:#"qty"];
//If you not have any custom class than use Dictionary
NSMutableDictionary *cart = [[NSMutableDictionary alloc] init];
[cart setObject:[[products objectAtIndex:i] valueForKey:#"pid"] forKey:#"ArticleID"];
[cart setObject:[[products objectAtIndex:i] valueForKey:#"pid"] forKey:#"Quantity"];
}
Now add this carts array to Dictionary with key
[dict setObject:carts forKey:#"carts"];
NSArray *pid = [products valueForKey:#"pid" ];
NSArray *qty = [products valueForKey:#"qty"];
NSMutableArray *carts = [[NSMutableArray alloc] initWithCapacity:products.count];
for(NSInteger i=0; i<products.count; i++) {
NSMutableDictionary *cart = [[NSMutableDictionary alloc] init];
NSMutableDictionary *cartemp = [[NSMutableDictionary alloc] init];
[cart setObject:[pid objectAtIndex:i] forKey:#"ArticleId"];
[cart setObject:[qty objectAtIndex:i] forKey:#"Quantity"];
[cartemp setObject:cart forKey:#"Cart"];
[carts addObject:cartemp];
}
[dict setObject:carts forKey: #"Carts"];
I have made a quiz using Obj.C. What I want to to do is make the questions appear in random order. I have tried many methods but so far I have failed.
+ (NSArray *) myArray
{
static NSArray *theArray;
if (!theArray)
{
theArray = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil];
}
return theArray;
}
+ (NSArray *) questions {
return #[
#{#"question":
Here's how I generate a random array from another array.
NSArray *myArray = #[object1, object2, object3, ..., objectN];
NSMutableArray *arrayToTakeItemsFrom = [[NSMutableArray alloc] arrayWithArray:myArray];
NSMutableArray *randomOrderedArray = [NSMutableArray new];
while (arrayToTakeItemsFrom.count > 0) {
int randomIndex = arc4random_uniform(arrayToTakeItemsFrom.count);
MyObjectClass *swapObject = [arrayToTakeItemsFrom objectAtIndex:randomIndex];
[arrayToTakeItemsFrom removeObject:swapObject];
[randomOrderedArray addObject:swapObject];
}
//Now randomOrderedArray has everything myArray has in it but in a random order, and always works for any size array including an empty array
What I am trying to achieve is the following, store one value (keyword) under the key (account) in one dictionary and then store that dictionary under another dictionary with the key (list) and finally that dictionary under another dictionary under the key (allKeys).
- (void)addFilterForAccount:(NSString *)account forKeyword:(NSString *)keyword inList:(NSString *)list {
//Set properties for NSStrings
account = _accountName;
keyword = _textField2.text;
list = _listName;
//Clear the textField
_textField2.text = #"";
//Store this information into a dictionary/array
_keys = [[NSMutableDictionary alloc] init];
[_keys setObject:keyword forKey:account];
_keywords = [[NSMutableDictionary alloc] init];
[_keywords setObject:_keys forKey:list];
_filters = [[NSMutableDictionary alloc] init];
[_filters setValue:_keywords forKey:#"allKeys"];
//nomenclature would be:
//
//_filters[#"allKeys"][#"//listName\\"][#"//accountName\\"]
NSLog(#"%#", _filters);
}
I have the following code that should do what I want, but the problem at this time is that it doesn't allow for multiple keys. So if I want to store multiple keywords under one list, when I log out it should be like this:
2014-09-07 18:31:12.562 Filterfeed[4050:124143] {
allKeys = {
"Breaking News" = (
{
BreakingNews = romney
obama
bush
clinton;
}
);
};
}
But right now it is just one name, and it gets replaced each time I invoke this method.
What actually prints out:
2014-09-07 18:31:12.562 Filterfeed[4050:124143] {
allKeys = {
"Breaking News" = (
{
BreakingNews = romney;
}
);
};
}
You want this method to be:
- (void)addFilterForAccount:(NSString *)account forKeyword:(NSString *)keyword inList:(NSString *)list {
//Set properties for NSStrings
account = _accountName;
keyword = _textField2.text;
list = _listName;
//Clear the textField
_textField2.text = #"";
//Store this information into a dictionary/array
_keys = [[NSMutableDictionary alloc] init];
NSMutableArray *array = [NSUserDefualts standardUserDefaults] objectForKey: list];
[array addObject: keyword];
[[NSUserDefualts standardUserDefaults] setObject: array forKey: list];
[[NSUserDefualts standardUserDefaults] synchronize];
NSMutableArray *array2 = [NSUserDefualts standardUserDefaults] objectForKey: list];
[_keys setObject:array2 forKey:account];
_keywords = [[NSMutableDictionary alloc] init];
[_keywords setObject:_keys forKey:list];
_filters = [[NSMutableDictionary alloc] init];
[_filters setValue:_keywords forKey:#"allKeys"];
//nomenclature would be:
//
//_filters[#"allKeys"][#"//listName\\"][#"//accountName\\"]
NSLog(#"%#", _filters);
}
I am very new to Objective-C and iOS programming so be gentle :)
I am trying to add an nsmutabledictionary to and nsmutablearray. I am succeeding but not with the results I was hoping for. Here is my code :
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
NSMutableDictionary *messages = [[NSMutableDictionary alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] init];
[dictionary setValue:#"lat1" forKey:#"lat"];
[dictionary setValue:#"long1" forKey:#"long"];
[dictionary setValue:#"alt1" forKey:#"alt"];
[messages setObject:dictionary forKey:#"messages"];
[array addObject:messages];
[dictionary setValue:#"lat2" forKey:#"lat"];
[dictionary setValue:#"long2" forKey:#"long"];
[dictionary setValue:#"alt2" forKey:#"alt"];
[messages setObject:dictionary forKey:#"messages"];
[array addObject:messages];
NSLog(#"%#",array);
NSLog(#"%lu",(unsigned long)[array count]);
Here is the NSLog output:
2014-06-05 10:29:27.377 dicttest[4863:60b] (
{
messages = {
alt = alt2;
lat = lat2;
long = long2;
};
},
{
messages = {
alt = alt2;
lat = lat2;
long = long2;
};
}
)
2014-06-05 10:29:27.386 dicttest[4863:60b] 2
Here is what I was hoping to achieve:
2014-06-05 10:29:27.377 dicttest[4863:60b] (
{
messages = {
alt = alt1;
lat = lat1;
long = long1;
};
},
{
messages = {
alt = alt2;
lat = lat2;
long = long2;
};
}
)
2014-06-05 10:29:27.386 dicttest[4863:60b] 2
If I the dictionary straight to the array (instead of add the dictionary to messages and then adding that to the array) then I get the output I am looking for. Can somebody explain to me exactly what I am doing wrong?
It looks to me like you want:
An array
At index 0:
A dictionary with a single key "messages"
A dictionary with keys "alt", "lat", and "long"
At index 1:
A dictionary with a single key "messages"
A dictionary with keys "alt", "lat", and "long"
The data in the second array entry should use the same keys, but different data. As the others have pointed out, your mistake is using a single dictionary "dictionary"
When you add an object to a collection like a dictionary or array, the collection holds a pointer to the object, not a copy of the object. If you add the same object to a collection more than once, you have 2 pointers to the same object, not 2 unique objects.
When you add your "dictionary" object, to your structure, change it, and add it again, you are not getting the result you expect because both entries in your structure point to a single dictionary. When you change the values, it changes in both places.
The same goes for your "messages" dictionary. You need 2 of those as well.
Fix your code by adding new dictionaries, dictionary2 and messages2:
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
NSMutableDictionary *dictionary2 = [[NSMutableDictionary alloc] init];
NSMutableDictionary *messages = [[NSMutableDictionary alloc] init];
NSMutableDictionary *messages2 = [[NSMutableDictionary alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] init];
[dictionary setValue:#"lat1" forKey:#"lat"];
[dictionary setValue:#"long1" forKey:#"long"];
[dictionary setValue:#"alt1" forKey:#"alt"];
[messages setObject:dictionary forKey:#"messages"];
[array addObject:messages];
[dictionary2 setValue:#"lat2" forKey:#"lat"];
[dictionary2 setValue:#"long2" forKey:#"long"];
[dictionary2 setValue:#"alt2" forKey:#"alt"];
[messages2 setObject: dictionary2 forKey:#"messages"];
[array addObject: messages2];
NSLog(#"%#",array);
NSLog(#"%lu",(unsigned long)[array count]);
You might also look at using object literal syntax, e.g.:
dictionary[#"lat"] = #"lat1";
dictionary[#"long"] = #"long1";
dictionary[#"alt"] = #"alt1";
messages[#"messages"] = dictionary;
If you didn't need the whole thing to be mutable, you could even do everything with one line:
NSMutableArray *array = [
#[
#{#"messages": #{#"lat": #"lat1", #"long": #"long1", #"alt": #"alt1"}},
#{#"messages": #{#"lat": #"lat2", #"long": #"long2", #"alt": #"alt2"}}
];
Or to make it mutable:
NSMutableArray *array = [
#[
[#{#"messages":
[#{#"lat": #"lat1", #"long": #"long1", #"alt": #"alt1"} mutableCopy]} mutableCopy],
[#{#"messages":
[#{#"lat": #"lat2", #"long": #"long2", #"alt": #"alt2"} mutableCopy]} mutableCopy]
] mutableCopy];
EDIT: to add contents dynamically, you could use a method like this: (assuming that array is an instance variable)
- (void) addMessageWithLat: (NSString *) latString
long: (NSString *) longString
alt: (NSString *) altString;
{
NSMutableDictionary *messages = [[NSMutableDictionary alloc] init];
NSDictonary *contents =
[#{#"lat": latString,
#"long": longString,
#"alt": altString}
mutableCopy];
messages[#"messages"] = contents;
[array addObject: messages];
}
The problem is that you are making adding the new values in the same object reference. So the new Value will replace the older one. Just add this line before [dictionary setValue:#"lat2" forKey:#"lat"];
dictionary = [NSMutableDictionary alloc]init];
and this line before the second instance of [messages setObject:dictionary forKey:#"messages"];
messages = [[NSMutableDictionary alloc] init];
We have an app that calls a SOAP web service and retrieves a long list of XML, which the app then parses into an NSArray of NSDictionary objects. The NSArray contains a list of Rental Apartment information, each of which is stored into an NSDictionary.
The entire list may contain 10 different types of Apartments (i.e. 2-room, 3-room), and we need to split the NSArray into smaller NSArrays based on Room-Type, which has the key "roomType" in the NSDictionary objects.
Currently our algorithm is
Use [NSArray valueForKeyPath:#"#distinctUnionofObjects.room-type"]
to obtain a list of unique room-type values.
Loop through the list of unique room-type values
For each unique room-type value, use NSPredicate to retrieve matching items from the Original list
Our code is below (renamed for clarity):
NSArray *arrOriginal = ... ...; // Contains the Parsed XML list
NSMutableArray *marrApartmentsByRoomType = [NSMutableArray arrayWithCapacity:10];
NSMutableArray *arrRoomTypes = [arrOriginal valueForKeyPath:#"distinctUnionOfObjects.roomType"];
for(NSString *strRoomType in arrRoomTypes) {
NSPredicate *predicateRoomType = [NSPredicate predicateWithFormat:#"roomType=%#", strRoomType];
NSArray *arrApartmentsThatMatchRoomType = [arrOriginal filteredArrayUsingPredicate:predicateRoomType]; // TAKES A LONG TIME EACH LOOP-ROUND
[marrApartmentsByRoomType addObject:arrApartmentsThatMatchRoomType];
}
However, step 3 is taking a long time as the original list may contain large amount (>100,000) of items. It seems that NSPredicate goes through the entire list for each key value. Is there a more efficient way of splitting a large NSArray into smaller NSArrays, based on NSDictionary keys?
If the order of your splited Arrays is not important, i have a solution for you:
NSArray *arrOriginal;
NSMutableDictionary *grouped = [[NSMutableDictionary alloc] initWithCapacity:arrOriginal.count];
for (NSDictionary *dict in arrOriginal) {
id key = [dict valueForKey:#"roomType"];
NSMutableArray *tmp = [grouped objectForKey:key];
if (tmp == nil) {
tmp = [[NSMutableArray alloc] init];
[grouped setObject:tmp forKey:key];
}
[tmp addObject:dict];
}
NSMutableArray *marrApartmentsByRoomType = [grouped allValues];
This is quite performant
- (NSDictionary *)groupObjectsInArray:(NSArray *)array byKey:(id <NSCopying> (^)(id item))keyForItemBlock
{
NSMutableDictionary *groupedItems = [NSMutableDictionary new];
for (id item in array) {
id <NSCopying> key = keyForItemBlock(item);
NSParameterAssert(key);
NSMutableArray *arrayForKey = groupedItems[key];
if (arrayForKey == nil) {
arrayForKey = [NSMutableArray new];
groupedItems[key] = arrayForKey;
}
[arrayForKey addObject:item];
}
return groupedItems;
}
Improving #Jonathan answer
Converting array to dictionary
Maintaining the same order as it was in original array
//only to a take unique keys. (key order should be maintained)
NSMutableArray *aMutableArray = [[NSMutableArray alloc]init];
NSMutableDictionary *dictFromArray = [NSMutableDictionary dictionary];
for (NSDictionary *eachDict in arrOriginal) {
//Collecting all unique key in order of initial array
NSString *eachKey = [eachDict objectForKey:#"roomType"];
if (![aMutableArray containsObject:eachKey]) {
[aMutableArray addObject:eachKey];
}
NSMutableArray *tmp = [grouped objectForKey:key];
tmp = [dictFromArray objectForKey:eachKey];
if (!tmp) {
tmp = [NSMutableArray array];
[dictFromArray setObject:tmp forKey:eachKey];
}
[tmp addObject:eachDict];
}
//NSLog(#"dictFromArray %#",dictFromArray);
//NSLog(#"Unique Keys :: %#",aMutableArray);
//Converting from dictionary to array again...
self.finalArray = [[NSMutableArray alloc]init];
for (NSString *uniqueKey in aMutableArray) {
NSDictionary *aUniqueKeyDict = #{#"groupKey":uniqueKey,#"featureValues":[dictFromArray objectForKey:uniqueKey]};
[self.finalArray addObject:aUniqueKeyDict];
}
Hope, It will help when client wants final array in same order as input array.