Sorting two NSMutableArrays by 'nearest distance' first - iphone

I have two arrays, both full of NSString objects like this:
NSMutableArray *titles = [[NSMutableArray alloc] initWithObjects:#"Title1", #"Title2", #"Title3", #"Title4", #"Title5", nil];
NSMutableArray *distances = [[NSMutableArray alloc] initWithObjects:#"139.45", #"23.78", #"347.82", #"10.29", #"8.29", nil];
How can I sort both arrays by the nearest distance first?
So the results would be like this:
titles = #"Title5", #"Title4", #"Title2", #"Title1", #"Title3"
distances = #"8.29", #"10.29", #"23.78", #"139.45", #"347.82"
I understand that NSSortDescriptor can be used in these circumstances but after looking through the documentation, I am still unsure about how.

I would sort the distances this way...
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSArray *sortedDistances = [listItem sortedArrayUsingComparator: ^(id a, id b) {
NSNumber *aNum = [f numberFromString:a];
NSNumber *bNum = [f numberFromString:b];
return [aNum compare:bNum];
}];
I can't think of a particularly quick way to get the associated titles sorted, but this should work ...
NSMutableArray *sortedTitles = [NSMutableArray array];
NSDictionary *distanceTitle = [NSDictionary dictionaryWithObjects:titles forKeys:distances];
for (NSString *distance in sortedDistances) {
NSString *associatedTitle = [distanceTitle valueForKey:distance];
[sortedTitles addObject:associatedTitle];
}

You can use an NSComparator block and use NSArray's sortedArrayUsingComparator method. On that block, you will receive two objects to compare, and base on the comparison result, you can use NSMutableArray exchangeObjectAtIndex:withObjectAtIndex: method to change the values of titles.

Here is a sample how I sort an array of dictionaries by distance value:
-(void)reorderByDistance {
NSSortDescriptor *sortByName = [NSSortDescriptor sortDescriptorWithKey:#"distance" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortByName];
self.contentArray = [self.contentArray sortedArrayUsingDescriptors:sortDescriptors];
}
And my dictionary looks like this:
NSDictionary *dict1 = [[NSDictionary alloc] initWithObjectsAndKeys:#"1", #"id", #"Business #1", #"name", #"This business does some pretty remarkable things", #"description", #"Alley Bar", #"category", #"1.2", #"distance", nil];

One approach would be to create a dictionary mapping titles to distances, sort the distances, and then iterate through the distances to recreate the titles:
NSMutableArray *titles = [[NSMutableArray alloc] initWithObjects:#"Title1", #"Title2", #"Title3", #"Title4", #"Title5", nil];
NSMutableArray *distances = [[NSMutableArray alloc] initWithObjects:#"139.45", #"23.78", #"347.82", #"10.29", #"8.29", nil];
//Create a map of current titles to distances
NSDictionary *titleDistanceMap = [NSDictionary dictionaryWithObjects:titles forKeys:distances];
//Need to sort the strings as numerical values
[distances sortUsingComparator:^(NSString *obj1, NSString *obj2) {
return [obj1 compare:obj2 options:NSNumericSearch];
}];
//Now re-populate the titles array
[titles removeAllObjects];
for (NSString *distance in distances){
[titles addObject:[titleDistanceMap objectForKey:distance]];
}

Related

How to sort multi-dimensional array in objective-c?

I'm having a two dimensional array as follows
NSArray *cities = [NSArray arrayWithObjects:#"New Delhi",#"Karachi",#"Dhaka",#"Columbu", nil];
NSArray *distance = [NSArray arrayWithObjects:#"500",#"1400",#"1200",#"2800", nil];
NSMutableArray *details= [[NSMutableArray alloc]initWithCapacity:cities.count];
[details addObject:cities];
[details addObject:distance];
Now, the details array looks like
(
(
"New Delhi",
Karachi,
Dhaka,
Columbu
),
(
500,
1400,
1200,
2800
)
)
I need to sort the array with ascending order w.r.t distance array
ie,
(
(
"New Delhi",
Dhaka,
Karachi,
Columbu
),
(
500,
1200,
1400,
2800
)
)
how to do this?
I also need to sort the whole array by alphabetical order w.r.t cities array.
I tried using sortUsingComparator but, can't get the solution completely.
Any help would be appreciated.
self.arrayForRows = [[NSMutableArray alloc]init];
NSMutableArray *arrayForCities = [[NSMutableArray alloc]initWithObjects:#"Mumbai",#"Vizag",#"Hyderabad",#"Ahemdabad",#"Secunderabad", nil];
NSMutableArray *arrayForDistance = [[NSMutableArray alloc]initWithObjects:#"200",#"320",#"32",#"450",#"14", nil];
for (int i=0; i<arrayForCities.count; i++)
{
NSMutableDictionary *tempDicts = [[NSMutableDictionary alloc]init];
[tempDicts setObject:[arrayForCities objectAtIndex:i] forKey:#"names"];
[tempDicts setObject:[NSNumber numberWithInt:[[arrayForDistance objectAtIndex:i] intValue]] forKey:#"distance"];
[self.arrayForRows addObject:tempDicts];
}
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"distance" ascending:YES];
[self.arrayForRows sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
Structure is complicated. Make a class city containing name and distance. Then add objects of this class. Then you can sort using city.distance.

Sort multiple values in arrays

I have an array with multiple locations for different states.
{"location":"Lekki Phase 1","state":"abc","country":"Nigeria"},
{"location":"Lekki Phase 2","state":"xyz","country":"Nigeria"},
{"location":"Osapa London1","state":"def","country":"Nigeria"},
{"location":"Lekki Phase 2","state":"abc","country":"Nigeria"},
{"location":"Lekki Phase 3","state":"xyz","country":"Nigeria"},
{"location":"Osapa London 2","state":"def","country":"Nigeria"},..........
Now i can make an array for different states with no duplicate state , like
{"abc","xyz","def"}
But what i want is to display all locations state wise in a table.
How can i do this??
Using NSPredicate we can efficiently filter this . I have tried and tested working for me.
Here 'allDataArray' is array with dictionaries, You can replace your array here (the first one in your post)
NSMutableArray *allDataArray = [[NSMutableArray alloc] init];
NSMutableDictionary *dict1 = [[NSMutableDictionary alloc] init];
[dict1 setObject:#"Lekki Phase 1" forKey:#"location"];
[dict1 setObject:#"abc" forKey:#"state"];
[dict1 setObject:#"Nigeria" forKey:#"country"];
[allDataArray addObject:dict1];
dict1 = [[NSMutableDictionary alloc] init];
[dict1 setObject:#"Lekki Phase 2" forKey:#"location"];
[dict1 setObject:#"xyz" forKey:#"state"];
[dict1 setObject:#"Nigeria" forKey:#"country"];
[allDataArray addObject:dict1];
dict1 = [[NSMutableDictionary alloc] init];
[dict1 setObject:#"Lekki Phase 2" forKey:#"location"];
[dict1 setObject:#"abc" forKey:#"state"];
[dict1 setObject:#"Nigeria" forKey:#"country"];
[allDataArray addObject:dict1];
dict1 = [[NSMutableDictionary alloc] init];
[dict1 setObject:#"Lekki Phase 3" forKey:#"location"];
[dict1 setObject:#"xyz" forKey:#"state"];
[dict1 setObject:#"Nigeria" forKey:#"country"];
[allDataArray addObject:dict1];
//NSLog(#"%#",allDataArray);
NSArray *state = [NSArray arrayWithObjects:#"abc",#"xyz", nil];
NSMutableArray *locationInState = [[NSMutableArray alloc] initWithCapacity:[state count]];
for(int i=0; i< [state count]; i++)
{
NSMutableArray *filteredarray = [[allDataArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(state == %#)", [state objectAtIndex:i]]] mutableCopy];
for(int j=0; j<[filteredarray count];j++)
{
NSDictionary *dict = [filteredarray objectAtIndex:j];
[filteredarray replaceObjectAtIndex:j withObject:[dict valueForKey:#"location"]];
}
[locationInState addObject:filteredarray];
}
NSLog(#"%#",locationInState);
Here locationInState array contains all location for filetred state. You can map them easily by index.
Result is
(
(
"Lekki Phase 1",
"Lekki Phase 2"
),
(
"Lekki Phase 2",
"Lekki Phase 3"
)
)
First It not array but it is Dictionary.
NSMutableDictionary * newDict = [NSMutableDictionary dictionaryWithCapacity:[OLdDict count]];
for(id item in [OLdDict allValues]){
NSArray * keys = [OLdDict allKeysForObject:item];
[newDict setObject:item forKey:[[OLdDict allKeysForObject:item] objectAtIndex:0]];
}
NSMutableDictionaries can act as uniquing collections (because they replace objects for keys if the same key is used twice). We can also take advantage of the fact that NSString is generally a constant address location, and funnel each one of those dictionaries into an array. To unique out each array of dictionaries, it would be far easier to wrap them in an object, but here goes:
-(void)uniquingSort {
//Setup collections for the uniquing process
NSMutableArray *datasource = //...
NSMutableIndexSet *hits = [NSMutableIndexSet indexSet];
NSMutableDictionary *uniquingDict = #{}.mutableCopy;
//Setup an index for the indexed set
int idx = 0;
//iterate through the array of dictionaries
for (NSArray *arrOfDicts in datasource) {
//get the dictionary we want to unique against
NSDictionary *innerDict = arrayOfDicts[1];
//do we have a dupe? If so, add its index to the index set
if (uniquingDict[innerDict[#"state"]] != nil)
[hits addIndex:idx];
uniquingDict[innerDict[#"state"]] = innerDict[#"state"];
idx++;
}
//cut out all the hits till we are only uniqued for the "state" key
[datasource removeObjectsAtIndexes:hits];
}

i had 2 arrays with objects and the same nameobject should be cancelled only one time

I have input as two arrays shown below
NSArray *array1=[[NSArray alloc]initWithObjects:#"1",#"2",#"3", nil];
NSArray *array2=[[NSArray alloc]initWithObjects:#"1",#"2",#"1", nil];
the output should resemble like this.
the same element should be cancelled only one time.
NSArray *array3=[[NSArray alloc]initWithObjects:#"1",#"2", nil];
THANKS IN ADVANCE.....
NSArray *array1 = #[#"1",#"2",#"3"];
NSArray *array2 = #[#"1",#"2",#"1"];
NSMutableSet *allElemets = [NSSet setWithArray:array1];
[allElemets addObjectsFromArray:array2];
This will return you all elements without duplicates.
In this case it will be
#"1",#"2",#"3"
Edit:
This will return the intersection of the arrays
NSMutableSet *set1 = [NSMutableSet setWithArray:array1];
NSSet *set2 = [NSSet setWithArray:array2];
[set1 intersectSet:set2];
Use NSCountedSet for the above situation
NSMutableArray *array1=[[NSMutableArray alloc]initWithObjects:#"r",#"a",#"r",#"r",#"r", nil];
NSArray *array2=[[NSArray alloc]initWithObjects:#"b",#"c",#"r", nil];
NSMutableSet *setOne = [NSMutableSet setWithArray: array1];
NSSet *setTwo = [NSSet setWithArray: array2];
[setOne unionSet:setTwo];
NSArray *arrayOneResult = [setOne allObjects];
NSLog(#"%#",arrayOneResult);
NSMutableArray *resultArray = [[NSMutableArray alloc]init];
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:arrayOneResult];
for (id item in set)
{
NSCountedSet *set1 = [[NSCountedSet alloc] initWithArray:array1];
NSCountedSet *set2 = [[NSCountedSet alloc]initWithArray:array2];
int diff = abs([set1 countForObject:item] - [set2 countForObject:item]);
for (int i = 0 ;i < diff ;i++ ) {
[resultArray addObject:item];
}
}
NSLog(#"the array : %#",resultArray );
f you are fine with sets instead of arrays, you can use NSMutableSet instead of NSArray. NSMutableSet has nice methods like intersectSet: and minusSet:
if([[array1 objectAtIndex:i] isEqualToString:[array2 objectAtIndex:i]])
{
[array2 removeObjectAtIndex: i];
NSLog(#"same element removed.");
}
array3 = [firstArray arrayByAddingObjectsFromArray:secondArray];
or
NSMutableSet *set = [NSMutableSet setWithArray:array1];
[set addObjectsFromArray:array2];
array3 = [set allObjects];
Two arrays are compared and duplicate values are removed, you get your values.
Here tHe Code goes
EDIt: This WOuld remove the Duplicate Value add Unique value.
NSArray *array1=[[NSArray alloc]initWithObjects:#"1",#"2",#"3", nil];
NSArray *array2=[[NSArray alloc]initWithObjects:#"1",#"2",#"1", nil];
//Here Create nEw Array with Arra1
NSMutableArray * newArray =[[NSMutableArray alloc] initWithArray:array1];
for(int index=0; index<[array2 count];index++)
{
id object =[array2 objectAtIndex:index];
if(![newArray containsObject:object])//this methods Returns YES/NO
{
[newArray addObject: object];
}
}

How do I sort a NSMutableArray by NSString length?

I have a NSMutableArray containing NSStrings of various lengths. How would I go about sorting the array by the string length?
See my answer to sorting arrays with custom objects:
NSSortDescriptor *sortDesc= [[NSSortDescriptor alloc] initWithKey:#"length" ascending:YES];
[myArray sortUsingDescriptors:#[sortDesc]];
This is how I did it (love me some blocks!)
_objects = [matchingWords sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSNumber *alength = [NSNumber numberWithInt:((NSString*)a).length];
NSNumber *blength = [NSNumber numberWithInt:((NSString*)b).length];
return [alength compare:blength];
}];

Algorithm: Keeping count of key/value pair in NSDictionary

Being new to Cocoa, and probably not knowing all of the potential classes available that already have this functionality neatly wrapped in an OO class, here's an algorithm inquiry. What's the best bet to count how many times a particular key occurs in an array of multiple NSDictionary instances?
Essentially my data structure (in this case an NSArray) might contain several NSDictionary instances at any given time, each one having the same keys, potentially different values. Some values repeat. I'd like to be able to know how many times a particular key/value appears. Example:
{
foo => 1,
bar => 2
}
{
foo => 1,
bar => 3
}
{
foo => 2,
bar => 1
}
In this case I'm interested that foo=>1 occured 2 times and foo=>2 occured 1 time. Is building an instance of NSCountedSet the best way to go about this? Perhaps a C linked-list?
You may want to rethink how you are structuring your data. I'd track something like this while adding to the NSArray instead of trying to discover it at a later time. You might create a new class to handle adding and removing the data so that you can keep your own counts of the data.
NSDictionary * dict1 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:1], #"foo",
[NSNumber numberWithInt:2], #"bar", nil];
NSDictionary * dict2 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:1], #"foo",
[NSNumber numberWithInt:3], #"bar", nil];
NSDictionary * dict3 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:2], #"foo",
[NSNumber numberWithInt:1], #"bar", nil];
NSArray * arrayOfDictionaries = [[NSArray alloc] initWithObjects:
dict1, dict2, dict3, nil];
// count all keys in an array of dictionaries (arrayOfDictionaries):
NSMutableDictionary * countKeys = [[NSMutableDictionary alloc] initWithCapacity:0];
NSCountedSet * counts = [[NSCountedSet alloc] initWithCapacity:0];
NSArray * keys;
NSString * pairString;
NSString * countKey;
for (NSDictionary * dictionary in arrayOfDictionaries)
{
keys = [dictionary allKeys];
for (NSString * key in keys)
{
pairString = [NSString stringWithFormat:#"%#->%#", key, [dictionary valueForKey:key]];
if ([countKeys valueForKey:pairString] == nil)
{
[countKeys setValue:[NSString stringWithString:pairString] forKey:pairString];
}
countKey = [countKeys valueForKey:pairString];
{ [counts addObject:countKey]; }
}
}
NSLog(#"%#", counts);
[counts release];
[countKeys release];
[arrayOfDictionaries release];
[dict1 release];
[dict2 release];
[dict3 release];
NSCountedSet *keyCounts = [NSCountedSet set];
for (NSDictionary *dict in myDictionaries)
[keyCounts unionSet:[NSSet setWithArray:[dict allKeys]]];