Adding every key's first object from NSDictionary to NSMutableArray - ios

I have an NSMutableDictionary, that contains custom (PNMessage) objects that also contain an NSDictionary and two other string object. Every key represents a username and every object represents a message, that belongs to a key.
I would like to copy the newest objects that belongs to a particular user (key) into a new NSMutableArray. For example: i wanna copy the newest object that belongs to the key: Tim and Bill.
I know dictionaries are unsorted, but i store a string with the date in every dictionary (keyCreateDate) inside the objects, therefore it's possible to loop through them and identify the recent object.
This is how the sorted message dict looks like in the log:
{
tim = (
"PNMessage (0x1811a340): <message: {\n keyCreateDate = \"06/09/14 21:24\";\n keyMessage = \"Lorem ipsum\";\n keyRecieverChannel = bill;\n keySenderUser = tim;\n}, date: (null), channel: main>",
"PNMessage (0x16526ce0): <message: {\n keyCreateDate = \"06/09/14 21:31\";\n keyMessage = \"Lorem ipsum dolor\";\n keyRecieverChannel = tim;\n keySenderUser = bill;\n}, date: (null), channel: main>",
);
bill = (
"PNMessage (0x18109b00): <message: {\n keyCreateDate = \"06/08/14 21:23\";\n keyMessage = Hello World;\n keyRecieverChannel = bill;\n keySenderUser = tim;\n}, date: (null), channel: main>",
"PNMessage (0x18104070): <message: {\n keyCreateDate = \"06/09/14 21:23\";\n keyMessage = \"Hello Bill\";\n keyRecieverChannel = bill;\n keySenderUser = kyra;\n}, date: (null), channel: main>",
);
dave = (
"PNMessage (0x1811a340): <message: {\n keyCreateDate = \"06/09/14 21:24\";\n keyMessage = \"Lorem ipsum\";\n keyRecieverChannel = bill;\n keySenderUser = tim;\n}, date: (null), channel: main>",
"PNMessage (0x16526ce0): <message: {\n keyCreateDate = \"06/09/14 21:31\";\n keyMessage = \"Lorem ipsum dolor\";\n keyRecieverChannel = tim;\n keySenderUser = bill;\n}, date: (null), channel: main>",
);
}
And i need an array like this
NSMutableArray *sampleArray = #[newestObjectofTim, newestObjectofBill, newestObjectofDave];
I think i should loop through every key's every object, check the keyCreateDate inside every object and add the recent to the sampleArray. The key name that we need in the main dictionary is user.
I started with this, but get stucked after the first lines. As a first step i tried to check every PNMessage object in the self.messagesDictionary (which has the user key) and then log the 1. object. It runs in the simulator, but i can't see the log, so it's sure that's wrong.
for(PNMessage *object in [self.messagesDictionary objectForKey:#"user"])
{
[object.message objectAtIndex:0];
NSLog(#"the object is %#", object.message);
}
UPDATE:
The accepted answer is correct, but Duncan's answer also contain useful, relevant suggestion.

Something like this will give you a dictionary containing the most recent message for each user string.
//Get newest message from each user. User(NSString *) => PNMessage*
NSMutableDictionary *newestMessageByUser = [NSMutableDictionary dictionary];
for (NSString *user in self.messagesDictionary) {
NSArray *messages = self.messagesDictionary[user];
newestMessageByUser[user] = messages.firstObject;
}

It looks to me like your structure is a little different than your description. I see:
An outer dictionary, with keys like "tim", "bill", and "dave"
Each entry in your dictionary appears to be an array of PNMessage objects. You never mentioned the array.
In the small sample you posted, the array of message objects appears to be sorted in order of oldest created date to newest.
It isn't completely clear from your description how your PNMessage object is structured. Your log says the message objects contain values for "keyCreateDate" and "keyMessage". Does the PNMessage object contain a dictionary, and those are the keys that are in the dictionary in each message object?
Can you post the header for your PNMessage object class? And are you keys in the PNMessage "keyCreateDate" and "keyMessage" or "CreateDate" and "Message" (Do they have the prefix "key" or not?
If your arrays of messages are sorted oldest-to-newest as your data suggests then #ChrimsonChris's code is 99% of the way there. However, the last line should read
newestMessageByUser[user] = messages.lastObject;
(Since you said you wanted the newest message for each user in your outer dictionary of users and it looks to me like the first entry in each array is the oldest and the last entry is the newest.)
By the way, his for loop is a little confusing at first. The line
for (NSString *user in self.messagesDictionary)
Uses fast enumeration on a the outer dictionary.
If you search on the string "Using fast enumeration with a dictionary" in the Xcode docs, you will find this bit of information:
The behavior for fast enumeration varies slightly based on the type of
collection. Arrays and sets enumerate their contents, and dictionaries
enumerate their keys.
So that for loop will give you one key at a time from your outer dictionary.
If your message objects are not in an array, or the array is not sorted by create date, then you'll have to adapt ChrimsonChris's code.

Related

Add objects to NSMutableArray that don't reference each other

First question:
Basically I'm attempting to add NSMutableDictionary objects to an NSMutableArray by using a method addItem.
-(void)addItem:(id)ObID name:(id)ObName description:(id)ObDesc menuName:(id)ObmName menuID:(id)ObmID mid:(id)Obmid pod:(id)pod type:(id)obType price:(id)price
{
NSMutableDictionary *itemToAdd = [[NSMutableDictionary alloc]init];
[itemToAdd setValue:ObID forKey:#"id"];
[itemToAdd setValue:ObName forKey:#"name"];
[itemToAdd setValue:ObDesc forKey:#"description"];
[itemToAdd setValue:ObmName forKey:#"mname"];
[itemToAdd setValue:ObmID forKey:#"menu_id"];
[itemToAdd setValue:Obmid forKey:#"mid"];
[itemToAdd setValue:pod forKey:#"POD"];
[itemToAdd setValue:obType forKey:#"Type"];
[itemToAdd setValue:price forKey:#"Price"];
[itemToAdd setValue:#"1" forKey:#"Amount"];
[items addObject:itemToAdd];
}
However when this method is called again later the next object that is added to the overwrites the value of an object with the same keys. Im aware this is because an array simply retains a memory address, hence when the same object simply with a different type "obType" is added all of those values change.
How am I able to add objects to an NSMutableArray without them referencing each other?
Example:
Output after adding one object:
{
Amount = 1;
POD = BOTH;
Type = {
0 = Vegetarian;
1 = Aussie;
};
description = "Online Special Only - Two Large Pizza's $25 (No half halves or extra toppings!) Enjoy!";
id = 7596;
"menu_id" = 112;
mid = 112;
mname = "Deal";
name = "Hungry? Two Large Pizzas!";
}
)
Output after adding another object of same ID however of a different type:
{
Amount = 1;
POD = BOTH;
Type = {
0 = "Plain";
1 = Aussie;
};
description = "Online Special Only - Two Large Pizza's $25 (No half halves or extra toppings!) Enjoy!";
id = 7596;
"menu_id" = 112;
mid = 112;
mname = "Deal";
name = "Two Large Pizzas!";
},
{
Amount = 1;
POD = BOTH;
Type = {
0 = "Plain";
1 = Aussie;
};
description = "Online Special Only - Two Large Pizza's $25 (No half halves or extra toppings!) Enjoy!";
id = 7555;
"menu_id" = 112;
mid = 112;
mname = "Deal";
name = Two Large Pizzas!";
}
)
As can be seen both object types have changed.
Ok so I solved the problem, not 100% sure why.
I was passing in the type object as an NSMutableDictionary which if an item had 2 variations, one object in the dictionary would have a key of 0 the other 1 when passed in like this they overwrite each other.
Passing just the values in as an NSArray fixes this.
Done by passing in:
NSArray * values = [selectedItemOptions allValues];
Thanks all who helped.
I can't give you the answer, there is not enough information provided to do that, but maybe I can help you figure it out yourself. You write:
Im aware this is because an array simply retains a memory address, hence when the same object simply with a different type "obType" is added all of those values change.
That is not what happens in your code, though there are situations where what is in an array may appear to change - and that is what you are probably seeing. The line:
NSMutableDictionary *itemToAdd = [[NSMutableDictionary alloc]init];
generates a new, never before seen, mutable dictionary. You can log the address (effectively the identity(1)) of this object by adding:
NSLog(#"Created itemToAdd: %p", itemToAdd);
after the above line. The %p format will produce the address of the newly created object, and every object has a unique address. When you then get to the line:
[items addObject:itemToAdd];
The new dictionary is added to whichever array is referenced by items - and that is very important. Any variable, such as items and itemToAdd, references an object, it is not the object itself. So the array being referenced by items could change between calls to addItem:. You should check this and you can do that by adding(2):
NSLog(#"addItem called, items = %p containing %ld element", items, items.count);
for(id someThing in items)
NSLog(#" element: %p", someThing);
to the start of your method. This will first tell you the address of the array currently referenced by items and how many items that array contains. The for loop will then output the address of each element of the array (if there are any).
Further note that addObject: will always add an item to the array, it doesn't matter if the same item is already in the array - add it twice and the array appears to have two elements, both of which reference the same item. When the above code displays the count it should be increasing unless you are removing elements from the array somewhere.
Add those statements and look at the results. Among the possibilities that would produce the results you are reporting:
The array referenced by items is changing
The dictionary you add is being removed
Etc.
HTH
(1) Addresses can be reused, after an object is no longer required and destroyed by ARC its memory may be reused for a new object. So the address does not strictly identify a unique object, you just need to be aware of this
(2) I am assuming 64-bit here, if not you need to change the %ld format or add a cast.

Sorted NSMutableArray from NSArray

I have an NSArray called message, it contains an unsorted list of custom objects. Every object has the same structure, it has an NSDictionary and two other string in it.
Every object is a message and i would like to sort them based on the dictionary key keySenderUser and keyReceiverUser. The new array should contain arrays, each array in the new array should represent the messages from a different user except the current user.
For example i have 50 message in the base message array, it contains messages from Bill, John, Anna, Taylor and the "current user". I would like to put every message into a new array where John is the keySenderUser or keyRecieverUser and put to a new mutable array where i collect the messages of the users and do the same with Bill, John, Anna and Taylor. The result should be an array that looks like this:
NSMutableArray *messagesFromUsers = #[messagesOfBill, messagesOfJohn, messagesOfAnna, messagesOfTaylor];
For example the messagesOfBill must contains the messages where Bill is the sender or the receiver that we know from the dictionary values. If Bill is the sender, current user is the receiver, if Bill is the receiver, current user is the sender. The other part always is the current user. There is no messages where Anna is the sender and Bill is the receiver.
As a first step i think i need a list with the usernames and then iterate through the all messages and create a new array for every user and collect these arrays into a new array, but honestly i don't have any idea how should i do it. I can remove objects from the message array based on one dictionary key, but that's all. I'm not experienced to figure it out alone.
UPDATE:
This is how one object looks like in the message array:
NSLog(#"First message: %#", [message objectAtIndex:0]);
// result
First message: PNMessage (0x175d49f0): <message: {
keyCreateDate = \"06/08/14 21:23\";
keyMessage = "Lorem Ipsum ";
keyRecieverChannel = vidra;
keySenderUser = currentUsersName;
}, date: (null), channel: currentUsersName>
You're definitely on the right track with your idea at the end of your post. First, get the list of all users.
NSMutableArray *allUsers = [[NSMutableArray alloc] init];
for(PNMessage *msg in message)
{
if(![allUsers containsObject:msg.keySenderUser])
[allUsers addObject:msg.keySenderUser];
else if(![allUsers containsObject:msg.keyReceiverUser])
[allUsers addObject:msg.keyReceiverUser];
}
[allUsers removeObject:currentUsersName]; // don't need current user in the array
Now you have a list of all the usernames. Loop through your message array for each user and divvy the messages. I would recommend a dictionary entry for every user with the key being their name and the object being a mutable array of their messages.
NSMutableDictionary *sortedMessages = [[NSMutableDictionary alloc] init];
for(NSString *user in allUsers)
{
NSMutableArray *messagesForUser = [[NSMutableArray alloc] init];
for(PNMessage *msg in message)
{
if([msg.keySenderUser isEqualToString:user] ||
[msg.keyReceiverUser isEqualToString:user])
{
[messagesForUser addObject:msg];
}
}
[sortedMessages setObject:messagesForUser forKey:user];
// if your messages list is exceptionally large, you could delete the messages you've already sorted here
// so that you don't have to look at messages you've already sorted
}
You now have an array with messages sorted by user. You could bring up the array of messages from Bill by doing
[sortedMessages objectForKey:#"Bill"];

RestKit post with NSArray of NSDictionary numbering

I am using RestKit to Post an object to the server.
The object contains an NSArray, which I convert to NSDictionaries before adding it to the parameters list (an NSDictionary)
the NSDictionary queryParams looks like this (printed out using NSLog, and removing some fields for clarity)
requestParams = {
"app_version" = "v1.0(1.006)";
"extended_information" = {
"travel_locations" = (
{
"Aircard_or_Tablet" = 0;
Country = "United States";
"End_Date" = "04/12/2013";
"Start_Date" = "03/12/2013";
}
);
"using_voice" = 0;
};
}
I then send it:
[objectManager postObject:nil path:server_path parameters:requestParams success:… failure:…]
The problem is that when RestKit creates the query string, it seems to increment the counter for each key in the dictionary, rather than per item in the array. The following is what is received by the server. I added a line break after each parameter for clarity:
app_version=v1.0(1.006)
&extended_information[travel_locations][0][Aircard_or_Tablet]=0
&extended_information[travel_locations][1][Country]=United States
&extended_information[travel_locations][2][End_Date]=04/12/2013
&extended_information[travel_locations][3][Start_Date]=03/12/2013
&extended_information[using_data]=0
The server is expecting (and I would expect) travel_locations to remain at [0] for all the keys in that one object, like this:
app_version=v1.0(1.006)
&extended_information[travel_locations][0][Aircard_or_Tablet]=0
&extended_information[travel_locations][0][Country]=United States
&extended_information[travel_locations][0][End_Date]=04/12/2013
&extended_information[travel_locations][0][Start_Date]=03/12/2013
&extended_information[using_data]=0
And, of course, if a second travel_location were there it would have [1], the third would have [2], and so forth.
Is this a bug in RestKit? Is there a better way I can be sending this request so that it does work? I do not have the option of changing the server.
To question is:
How can I have RestKit convert the NSDictionary for parameters properly so that it does not increment the array index identifier for each key in the array object?
Thanks,
-Nico

Sort array based on second array order

I have an array from a plist and each value contains an key and a string and a secondary array that I get from a json file online. I want to order the secondary array based on the keys in the first array.
I want to achieve something like this:
array1:
Item0 - EUR
- String
Item1 - USD
- String
Item2 - AUD
- String
etc
array2:
Item0 - AUD
- 123.242
Item1 - EUR
- 535.123
Item2 - USD
- 325.646
etc
I have the same key index on both but I want to get the value for the key index from array2 based on the order of the key index in array1.
I have researched online but I cannot find a suitable solution that I can understand how to implement it.
How can I implement this?
Here is the plist file - https://gist.github.com/iulianvarzaru/11c400ba1edf4a165082
And the json file - https://gist.github.com/iulianvarzaru/1915e02a9201c57f49b3
Given that the JSON file you've linked to doesn't contain an array but a dictionary, you can simply iterate over array1 from the plist file. Each element of that array is a dictionary with a "Cod" key and a "Descriere" key. Get the value for the "Cod" key and then simply use that value as the key into the dictionary from the JSON file.
NSDictionary* jsonFileDict = ...;
NSDictionary* jsonFileInnerDict = jsonFileDict[#"rate"];
for (NSDictionary* dict in array1)
{
NSString* code = dict[#"Cod"];
NSNumber* jsonNumber = jsonFileInnerDict[code];
// Do something with jsonNumber
}
It sounds like these are key-value pairs, in which case, you can convert it to a Map, and then do direct lookups.
If you can manipulate the JSON file as JSON, then it reduces a conversion, but may not be the most efficient implementation.
Caveats:
This method assumes that you wont have key overloading (which is possible in a numeric array, but not in a map)
This requires a conversion from one data structure to another
EDIT: (due to increased information by OP).
The JSON file you receive doesn't contain an array, it contains an object. Thus, all the values are direct-lookup. So, you can traverse your array in Obj-c, and directly access the corresponding values in the JSON.
Sorry about the lack of actual code-samples.
You are dealing with a dictionary in the response, not an array.
You should transform it to something like
{
#"currency": #"EUR",
#"value": 123.45
}
create and sort it it like
NSArray *keys = #[#"EUR",#"USD",#"AUD"];
NSDictionary *dict = #{#"AUD":#(123.242), #"EUR": #(535.123), #"USD": #(325.646)};
NSMutableArray *result = [#[] mutableCopy];
for (NSString *key in keys) {
[result addObject:#{#"value":dict[key], #"currency": key}];
}
NSLog(#"%#", result);
(
{
currency = EUR;
value = "535.123";
},
{
currency = USD;
value = "325.646";
},
{
currency = AUD;
value = "123.242";
}
)
Or write a model class that can handle this information.

iOS sort one array based on order of strings in another array

This is another very specific problem I am trying to solve.
I am pulling a list a twitter user accounts logged into the users settings application. This returns an array with the usernames in the correct order.
I then pass this array to this twitter API:
https://api.twitter.com/1.1/users/lookup.json
The returned array contain all the additional data I need for each user account logged in. The first array contains NSStrings (usernames), the returned array has been parsed to contain dictionaries that have a key and value for the username, name, and profile pic.
Problem now is that the order is completely different than the first array I passed.. This is expected behavior from Twitter, but it needs to be in the exact same order (I will be referencing the original index of the AccountStore which will match the first array, but not the new array of dictionaries).
How can I tell the new array to match the contained dictionaries to be the same order as the first array based on the username key?
I know this sounds confusing, so let me at least post the data to help.
Here is the first array output:
(
kbegeman,
indeedyes,
soiownabusiness,
iphonedev4me
)
Here is what the second array outputs:
(
{
image = "https://si0.twimg.com/profile_images/3518542448/3d2862eee546894a6b0600713a8de862_normal.jpeg";
name = "Kyle Begeman";
"screen_name" = kbegeman;
},
{
image = "https://si0.twimg.com/profile_images/481537542/image_normal.jpg";
name = "Jane Doe";
"screen_name" = iPhoneDev4Me;
},
{
image = "https://twimg0-a.akamaihd.net/profile_images/378800000139973355/787498ff5a80a5f45e234b79005f56b5_normal.jpeg";
name = "John Doe";
"screen_name" = indeedyes;
},
{
image = "https://si0.twimg.com/sticky/default_profile_images/default_profile_5_normal.png";
name = "Brad Pitt";
"screen_name" = soiownabusiness;
}
)
Due to the way Twitter returns the data, it is never the EXACT same order, so I have to check this every time I call these methods.
Any help would be great, would save my night. Thanks in advance!
You want the array of dictionaries be sorted by comparing screen_name value with your first array. Right? Also, the screen name may have different case than your username. Right?
I would use mapping dictionary:
Create dictionary from screen name to user dictionary:
NSArray *screenNames = [arrayOfUserDicts valueForKeyPath:#"screen_name.lowercaseString"];
NSDictionary *userDictsByScreenName = [NSDictionary dictionaryWithObjects:arrayOfUserDicts forKeys:screenNames];
Build final array by finding user dictionary for usernames in your array:
NSMutableArray *sortedUserDicts = [NSMutableArray arrayWithCapacity:arrayOfUsernames.count];
for (NSString *username in arrayOfUsernames) {
NSDictionary *userDict = [userDictsByScreenName objectForKey:username.lowercaseString];
[sortedUserDicts addObject:userDict];
}
First generate a mapping that maps the "screen_name" to the corresponding dictionary
in the second array:
NSDictionary *map = [NSDictionary dictionaryWithObjects:secondArray
forKeys:[secondArray valueForKey:#"screen_name"]];
Then you can create the sorted array with a single loop:
NSMutableArray *sorted = [NSMutableArray array];
for (NSString *name in firstArray) {
[sorted addObject:map[name]];
}
That sort order isn't something that could be easily replicated (i.e. it's not alpha, etc). Instead, you should just use that original NSArray as a guide to match data from the NSDictionary from Twitter. For example:
[twitterDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSInteger index = [yourLocalArray indexOfObject:obj];
if (index != NSNotFound) {
// You have a match, do something.
}
}];
lets name your arrays as firstArray and secondArray.
NSArray *sortedArray = [secondArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [#([firstArray indexOfObject:[obj1 objectForKey:#"name"]]) compare:#([firstArray indexOfObject:[obj2 objectForKey:#"name"]])];
}];

Resources