Sorted NSMutableArray from NSArray - ios

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"];

Related

IOS/Objective-C: Search string element with array of objects

As part of an autocomplete box, I am searching names within an array of contacts. However, after the user picks a name from the suggested List, I need to grab the id of the contact which is in the array of contact objects but not the array of names that appear in the suggest box. I've been working with just the names as that's what I want to display in the suggestion box but have an array of contacts as well.
How would I convert code below (probably using key values) to search the name dimensions of an array of objects instead of an array of names so as to keep track of the ids of the objects. I am kind of fuzzy on arrays and key values.
//the array being searched looks something like #[#"John", #"Dave", #"Sam", #"Xian", #"Ahmed", #"Johann"];
//I want to search the names in an array that looks something like:
(
{
first = "John";cid = 2;},
{
first = "Dave";cid = 44;},
{
first = "Xian";cid=99})
//this code works great to search names but I lose track ids. Thank you for any suggestions.
-(void)searchArray: (NSMutableArray*) array forString: (NSString *) term {
[_contactsSuggested removeAllObjects];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",term];
NSArray *tempArray = [array filteredArrayUsingPredicate:predicate];
_contactsSuggested = [NSMutableArray arrayWithArray:tempArray];
[_autocompleteTableView reloadData];
}
Create a Contact object. Give it a name property, an id property, and any other properties you need. Then write code that searches an array of Contact objects rather than just an array of names. You could then create a predicate using predicateWithBlock to filter the items that match your name property.

How can I implement my logic properly to populate my UITableView

Sorry guys, this problem I am running into is pretty trivial. I just can't wrap my head around it so hope someone can help me. Your help is really appreciated. I am getting JSON data through NSURLConnectDelegate with a web API. I get something like this back:
(
{
id = 340
name = Vicent },
{
id = 339
name = Johny },
{
id = 338
name = Eric }
)
and I save it in a NSMutableArray as a global variable. Now, I have a NSSet of "ids". For example:
{
340, 339
}
In the numberOfRowsInSection, I return the set's count. I am trying to load only the ids in the NSSet from the array with the data saved from the webAPI, so I do something like this in cellForRowIndexPath:
for (NSNumber *num in [set allObjects]) {
NSString *newString = [[savedArray objectAtIndex:indexPath.row]
NSString *new = [num stringValue];
if ([new isEqual:newString]) {
}}
How can I just populate the ids I want?
The JSON makes it look like you have an array of dictionaries, which is a reasonable data structure to use as the data source for a table view.
It sounds like you're trying to filter your array to only include the items that are in your set. Is that right?
If so, you could write code that would create a new array containing the subset of your array elements who's ID is also in your set. There are at least a half-dozen ways to do that. One fairly simple approach would be to use the NSArray method indexesOfObjectsPassingTest. You'd pass that method a block of code that would check each array element to see if it's id object was in your set.
That would give you an NSIndexSet with the indexes of the items in your array who's ID are in your set. Then you could use the NSArray method objectsAtIndexes to get an array of only the objects that are also in the set. Something like this (Assuming that your array of dictionaries is called savedArray and your set is called allObjects:
//get the indexes of items in the array savedArray who's id appears in the set allObjects
NSIndexSet *indexes = [savedArray indexesOfObjectsPassingTest:
^(NSDictionary *obj,
NSUInteger idx,
BOOL *stop)
{
return [allObjects member: obj[#"id"]] != nil;
}
];
//Now build an (immutable) array of just the objects who's ID are in the set
NSArray *subArray = [savedArray objectsAtIndexes: indexes];
The array subArray created above is immutable. If you need a mutable array you would need to make a mutable copy, which is a one-line change.
Disclaimer: I still struggle a little with block syntax, so the above might not be exactly correct, but it gives you the general idea.

Adding every key's first object from NSDictionary to NSMutableArray

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.

Find object by name in NSMutableArray

I have a generic person object with properties personName, lastName, and age. I am storing the user input into an NSMutableArray and I wanted to find a under by his/her name in the array. I have tried finding a bunch of different solutions but none that quite really work.
This is my main.m
#autoreleasepool {
char answer;
char locatePerson[40];
//Create mutable array to add users for retrieval later
NSMutableArray *people = [[NSMutableArray alloc] init];
do{
Person *newPerson = [[Person alloc]init];
[newPerson enterInfo];
[newPerson printInfo];
[people addObject:newPerson];
NSLog(#"Would you like to enter another name?");
scanf("\n%c", &answer);
}while (answer == 'y');
NSLog(#"Are you looking for a specific person?");
scanf("%c", locatePerson);
//This is where I need help
int idx = [people indexOfObject:]
}
This is very basic but I am new to objective-c and I wanted to try and find the user by name. The solutions I've seen have used the indexesOfObjectsPassingTest method. But I was wondering if I can't just use the indexOfObjectmethod the way I did there to locate a person by its name?
Any help is appreciated.
This is one of those hard problems you should avoid with some up-front design. If you know that you are putting things into a collection class and will need to get them out again based on some attribute (rather than by order of insertion) a dictionary is the most efficient collection class.
You can use a NSDictionary keyed with Person's name attribute. You can still iterate over all the objects but you will avoid having to search the whole collection. It can take a surprisingly long time to find a matching attribute in a NSArray! You wouldn't even have to change your Person object, just do
NSDictionary *peopleDictionary = #{ person1.name : person1, person2.name : person2 };
or add them one by one as they are created into a NSMutableArray.
You can try something like this assuming that 'name' is a property for your Person class.
NSUInteger i = 0;
for(Person *person in people) {
if([person.name isEqualToString:locatePerson]) {
break;
}
i++;
}

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