How to sort an NSArray with nested NSArray - objective-c

I have an instance of NSArray with instances of NSArray inside. The inner arrays contain only instances of NSString. I need to sort the nested arrays alphabetically by a selected index.
E.g.
#[
#[#"test",#"B",#"test2"],
#[#"bla",#"A",#"bla"],
#[#"xyz",#"C",#"123"]
]
User selects index 1, Ascending sort. Result should be:
#[
#[#"bla",#"A",#"bla"],
#[#"test",#"B",#"test2"],
#[#"xyz",#"C",#"123"]
]

You can use sortedArrayUsingComparator:
NSArray *array = #[#[#"test",#"B",#"test2"],
#[#"bla",#"A",#"bla"],
#[#"xyz",#"C",#"123"]];
NSInteger index = 1;
NSArray *sorted = [array sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return [obj1[index] compare:obj2[index] options:NSCaseInsensitiveSearch];
}];
Clearly, if the subarrays have different number of items or if they're not all strings, you might include some additional logic to handle that, but it illustrates the basic idea.

Related

Sort an NSArray by weighting two parameters

I have a NSArray of NSDictionary. I have two specific keys which I want to use to sort my array. Basically, I currently have two NSSortDescriptors:
descriptor = [NSSortDescriptor sortDescriptorWithKey:#"averageNote" ascending:NO];
descriptor2 = [NSSortDescriptor sortDescriptorWithKey:#"reviews.#count" ascending:NO];
They simply sort my array based on the average note of its elements, and then in the second place based on the number of reviews.
I would like to combine these two elements to make a more realistic ranking: for instance, 70% average note and 30% review count.
How can I achieve this?
this code will let the array sort itself by the rank you want,and the array will be sorted ascending ,if you want it by descending,change NSOrderedAscending to NSOrderedDescending.
NSMutableArray *array = [#[] mutableCopy];
[array sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
aClass a = obj1;
aClass b = obj2;
float rankOfA = a.averageNote*0.7 + a.reviewCount*0.3;
float rankOfB = b.averageNote*0.7 + b.reviewCount*0.3;
if (rankOfA > rankOfB) {
return NSOrderedAscending;
}
}];

Inserting NSDictionary into sorted NSMutableArray

I want to insert an NSDictionary object into an array and have the array sorted by objectForKey.
I managed to do this by inserting, re-sorting and reloading the array, which works fine, but I figured there would be a better method, which there is, I just don't know how to use it.
Current code:
-(NSMutableArray*)sortArray:(NSMutableArray*)theArray {
return [[theArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:#"n"
ascending:YES]]] mutableCopy];
}
returns the re-sorted array. Fine. Works perfectly, but... is it the most efficient?
From another question I gather that this is the method to use to find the index to insert into:
NSUInteger newIndex = [array indexOfObject:newObject
inSortedRange:(NSRange){0, [array count]}
options:NSBinarySearchingInsertionIndex
usingComparator:comparator];
[array insertObject:newObject atIndex:newIndex];
The problem is I have no idea how to use the comparator method, and if that's even possible when I want to sort by objectForKey for each index.
Can anyone exemplify the above method when the key of the object (newObject in above example) to sort by is, let's say:
[newObject objectForKey:#"sortByThis"];
Use NSSortDescriptor
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortByThisKey" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray = [myArray sortedArrayUsingDescriptors:sortDescriptors];
here is the simple example for comparator
- (NSComparisonResult)compareResulst:(CustomObject *)otherObject {
if(self.name isEqualToString:key)
{
return NSOrderedAscending;
}
else
{
return NSOrderedDescending
}
}
NSArray *sArray;
sArray = [unsArray sortedArrayUsingSelector:#selector(compareResulst:)];
this will sort the array. it will iterate through object and based on your if else it will move object up and down ...
Here's an example of sorting
//For make a we're adding 5 random integers into array inform of NSDictionary
//here, we're taking "someKey" to store the integer (converted to string object) into dictionary
NSMutableArray *array = [NSMutableArray array];
for(int i = 1; i<=5; i++) {
[array addObject:[NSDictionary dictionaryWithObject:[#(arc4random()%10) stringValue] forKey:#"someKey"]];
}
//create a sort descriptor to sort the array
//give the key in dictionary for which you want to perform sort
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"someKey" ascending:YES];
//we're doing self sort for mutable array, & that's it, you'll have a sorted array.
[array sortUsingDescriptors:#[sort]];
N.B.
1.Instead of "someKey" from above example you can set any key for which you want to perform sort.
2.There's some other methods for sorting, you can use the one base on your requirement.
3.see #[sort] in code. Its full version is, [NSArray arrayWithObject:sort];

NSDictionary order of keys and values

I know that in NSDictionary, there are no guarantees about the order of items when enumerating.
However, is it safe to expect that [[myDictionary allValues] objectAtIndex:index] will always be the matching value of key [[myDictionary allKeys] objectAtIndex:index]? (supposing that the dictionary is immutable).
Even if there is a very good chance that the two are going to be in the same order, Apple can change it at any time, because there is simply no guarantee of the order.
If you need two NSArrays, one with keys and one with values, with "parallel" indexes, a better approach to this would be building these arrays yourself by enumerating key-value pairs of the dictionary, and adding them to the two arrays that you build:
NSMutableArray *keys = [NSMutableArray array];
NSMutableArray *vals = [NSMutableArray vals];
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[keys addObject:key];
[vals addObject:obj];
}];
You can also get keys separately, and then get values in an array with parallel indexing by calling objectsForKeys:notFoundMarker: method:
NSArray *keys = [dict allKeys];
// In the call below, the marker [NSNull null] will not be used, because we know
// that all keys will be present in the dictionary.
NSArray *vals = [dict objectsForKeys:keys notFoundMarker:[NSNull null]];

Sort 2 arrays? Objective-c [duplicate]

I have several arrays that need to be sorted side by side.
For example, the first array has names: #[#"Joe", #"Anna", #"Michael", #"Kim"], and
and the other array holds addresses: #[#"Hollywood bld", #"Some street 3", #"That other street", #"country road"], where the arrays' indexes go together. "Joe" lives at "Hollywood bld" and so on.
I would like to sort the names array alphabetically, and then have the address array sorted alongside so they still go together, with "Hollywood bld" having same index as "Joe". I know how to sort one array alphabetical with
NSSortDescriptor *sort=[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:NO];
[myArray sortUsingDescriptors:[NSArray arrayWithObject:sort]];
But is there any easy way of getting the second array sorted using the appropriate order?
Create a permutation array, initially set to p[i]=i
Sort the permutation according to the name key of the first array
Use the permutation to re-order both arrays
Example: let's say the first array is {"quick", "brown", "fox"}. The permutation starts as {0, 1, 2}, and becomes {1, 2, 0} after the sort. Now you can go through the permutation array, and re-order the original array and the second array as needed.
NSArray *first = [NSArray arrayWithObjects: #"quick", #"brown", #"fox", #"jumps", nil];
NSArray *second = [NSArray arrayWithObjects: #"jack", #"loves", #"my", #"sphinx", nil];
NSMutableArray *p = [NSMutableArray arrayWithCapacity:first.count];
for (NSUInteger i = 0 ; i != first.count ; i++) {
[p addObject:[NSNumber numberWithInteger:i]];
}
[p sortWithOptions:0 usingComparator:^NSComparisonResult(id obj1, id obj2) {
// Modify this to use [first objectAtIndex:[obj1 intValue]].name property
NSString *lhs = [first objectAtIndex:[obj1 intValue]];
// Same goes for the next line: use the name
NSString *rhs = [first objectAtIndex:[obj2 intValue]];
return [lhs compare:rhs];
}];
NSMutableArray *sortedFirst = [NSMutableArray arrayWithCapacity:first.count];
NSMutableArray *sortedSecond = [NSMutableArray arrayWithCapacity:first.count];
[p enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSUInteger pos = [obj intValue];
[sortedFirst addObject:[first objectAtIndex:pos]];
[sortedSecond addObject:[second objectAtIndex:pos]];
}];
NSLog(#"%#", sortedFirst);
NSLog(#"%#", sortedSecond);
First off, you might want to re-consider an architecture that requires you to sort two arrays in a parallel fashion like this. But having said that, you can do it by creating a temporary array of dictionaries that keep the elements of the two arrays paired.
Then you sort the combined array, and extract the two arrays again, sorted as requested:
Original data:
NSArray *names = #[#"Joe", #"Anna", #"Michael"];
NSArray *addresses = #[#"Hollywood bld", #"Some street 3", #"That other street"];
The actual sorting code:
NSMutableArray *combined = [NSMutableArray array];
for (NSUInteger i = 0; i < names.count; i++) {
[combined addObject: #{#"name" : names[i], #"address": addresses[i]}];
}
[combined sortUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]]];
names = [combined valueForKey:#"name"];
addresses = [combined valueForKey:#"address"];
Notice that valueForKey: used on an array extracts a new array with the same size, populated with the properties of the objects in the original array. In this case, it create new arrays from the original ones, sorted as wanted.
This approach only requires a few lines of code and is easy to follow and debug, if needed.
The best way is to restructure your data so that you only have one array. In your example, it would make most sense to create a new class with both name and address, put those in an array and sort it by name.
You could probably do this by keeping track of indexes of objects before and after the sort but maybe it would be easier having a single object which hass all of these properties
Person : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *addresss;
Then you store these objects into a single array which you can sort by name, or address key paths

Objective-C NSMutableArray: Organize array of photos by date key

I have an array of dictionaries..looks like this.
(
{name = somename;
date = NSDate;
other_params = some other params;
},
...
)
How can I then sort the array items by NSDate (oldest to newest or vice versa). Do I just do a basic select-sort algorithm or is there a shorter way?
You can sort the array using descriptors or using comparators, whatever you feel more comfortable with. Here is an example of using comparators:
NSArray *sortedArray = [myArray sortedArrayUsingComparator: ^(id obj1, id obj2) {
return [[obj1 date] compare:[obj2 date]];
}];
Here's the sort descriptors option:
// Adjust the 'ascending' option to invert the sort order.
NSSortDescriptor *dateSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES];
// The sort descriptors have to go into an array.
NSArray *sortDescriptors = [NSArray arrayWithObject:dateSortDescriptor];
NSArray *sortedArray = [myArray sortedArrayUsingDescriptors:sortDescriptors];
You can also sort in place if you're using an NSMutableArray and don't need to keep its original order.