This question already has answers here:
Sorting two NSArrays together side by side
(4 answers)
Closed 7 years ago.
I'm trying to add fb friends scores table and I'm facing the problem by sorting them. From request, I'm getting two mutable arrays, one with friends name and one with their scores. Now I need to sort the scores in descending order (from highest to lowest). Sorting only scores is easy with this code:
NSSortDescriptor *scoreSorter= [[NSSortDescriptor alloc] initWithKey:#"self" ascending:NO];
[self.friendScoresArray sortUsingDescriptors:[NSArray arrayWithObject:scoreSorter]];
but if I do like this, I'm losing the connection between name and score, so I need to sort the names also now. Here's the example what happens now:
After request:
(Name - Score)
Name One - 110
Name Two - 120
Name Three - 100
After sorting the scores:
Name One - 120
Name Two - 110
Name Three - 100
This is obvious of course, but just a quick example of what I'm getting. Also, I've tried old method from C++ called bubble method. It's worked, but it's slow and sometimes giving ascending order, so that is bad practice in my opinion. So, what's the best way to sort out those to arrays without loosing connect between name and score? Thank you.
Pack each name and score into an object, stick that object into an array, sort that array based on the score member. I think this method should work quite well.
I would do something like this:
NSArray *_names = [NSArray arrayWithObjects:#"Jack", #"Zoe", #"Natalie", nil];
NSArray *_scores = [NSArray arrayWithObjects:#(110), #(120), #(100), nil];
NSMutableArray *_combined = [NSMutableArray array];
#define kName #"kName"
#define kScore #"kScore"
[_names enumerateObjectsUsingBlock:^(NSString * name, NSUInteger idx, BOOL *stop) {
NSNumber *_score = [_scores objectAtIndex:idx];
[_combined addObject:[NSDictionary dictionaryWithObjectsAndKeys:name, kName, _score, kScore, nil]];
}];
[_combined sortWithOptions:NSSortStable usingComparator:^NSComparisonResult(NSDictionary * obj1, NSDictionary * obj2) {
Float64 _score1 = [[obj1 valueForKey:kScore] doubleValue], _score2 = [[obj2 valueForKey:kScore] doubleValue];
if (_score1 < _score2) return NSOrderedDescending;
else return NSOrderedAscending;
}];
the input:
Jack - 110
Zoe - 120
Natalie - 100
the output:
Zoe - 120
Jack - 110
Natalie - 100
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]];
}];
Related
It's hard to explain why I need index of duplicate elements in array. When I tried to fetch the index of element in traditional way it shows only one index, but I need to fetch the all index of duplicate values
for ex:
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
int index = [array indexOfObject:element];
NSLog(#"index %d",index);
here if I try to fetch index of " one " it shows index is 0 but I need to get further indexes of one
You can fetch the index of duplicates like this:
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
if ([obj isEqualToString:#"one"])
{
NSLog(#"index %d",idx);
}
}];
int i,count=0;
for (i = 0; i < [array count]; i++) {
if element == [array objectAtIndex:i] {
indices[count++] = i;
}
}
Declare an empty array indices, and indices will contain all the indices of the given element.
NSString *element = #"one";
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
NSIndexSet *matchingIndexes = [array indexesOfObjectsPassingTest:^BOOL(NSString *obj, NSUInteger idx, BOOL *stop) {
return [obj isEqual:element];
}];
[matchingIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSLog(#"%ld", (long)idx);
}];
Ultimately I don't think the NSArray methods are going to help you here, so you're going to have to write some pretty basic code. There is probably a cleaner answer, but here is a fairly simply solution to the problem.
This just goes through the array, and creates an NSDictionary for each unique number. It assumes the array is sorted as your example was, so simply checks the prior index's value against the current index to see if they have changed. When they change, it knows it's done with that value and saves the dictionary to an array.
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
NSString *priorString = array[0];
NSMutableDictionary *duplicatesByKey = [[NSMutableDictionary alloc] init];
NSMutableArray *indexesOfDuplicates = [[NSMutableArray alloc] init];
int index = 0;
for (NSString *string in array) {
if ([priorString isEqualToString:string]) {
[indexesOfDuplicates addObject:[NSNumber numberWithInt:index]];
} else {
[duplicatesByKey setObject:indexesOfDuplicates forKey:priorString];
indexesOfDuplicates = [[NSMutableArray alloc] init];
[indexesOfDuplicates addObject:[NSNumber numberWithInt:index]];
}
priorString = string;
index ++;
}
[duplicatesByKey setObject:indexesOfDuplicates forKey:priorString];
I hope that helps.
Use
NSCountedSet * countedSet = [NSCountedSet setWithArray: array];
and
NSSet * uncountedSet = [NSSet setWithArray: array];
-- to create a counted set from your array, and a conventional NSSet.
Then:
[countedSet minusSet: uncountedSet];
countedSet will now contain only elements for the duplicates (if any), and the countForObject: method will return the number of duplicates (in excess of 1) for that element.
How would I take an array with long list of numbers that contains duplicates, so for instance:
NSArray *array = [NSArray arrayWithObjects:#"45", #"60", #"100",#"100", #"100", #"60"nil];
Just imagine that this is a HUGE list of random numbers. Now I'm sure that I have to use something like NSSet for this, but i'm not sure how to execute this. Also, once we identify the duplicates I'm guessing that I would then add those numbers to an array, and then call
[array count];
Any ideas?
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:array];
int duplicates = 0;
for (id object in set) {
if ([set countForObject:object] > 1) {
duplicates++;
}
}
This will calculate how many elements have a duplicate.
A sidenote, that array contains a bunch of strings, no numbers...
Anyway, if the goal is to get just the count you could use the following single line to get it.
NSUInteger diff = [array count] - [[array valueForKeyPath:#"#distinctUnionOfObjects.self"] count];
This uses KVC (Key-Value Coding) to get all distinct objects (that is ones without a dupe) counts them and gets the difference from the original count.
NSCountedSet is perfect for what you want to do.
NSArray *array = [NSArray arrayWithObjects:#"45", #"60", #"100",#"100", #"100", #"60",nil];
NSCountedSet *countedSet = [NSCountedSet setWithArray:array];
__block NSUInteger totalNumberOfDuplicates = 0;
[countedSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
NSUInteger duplicateCountForObject = [countedSet countForObject:obj];
if (duplicateCountForObject > 1)
totalNumberOfDuplicates += duplicateCountForObject;
NSLog(#"%# appears %ld times", obj, duplicateCountForObject);
}];
NSLog(#"Total number of duplicates is %ld", totalNumberOfDuplicates);
produces:
45 appears 1 times
60 appears 2 times
100 appears 3 times
Total number of duplicates is 5
Use filteredArrayUsingPredicate this use a predicate with your condition and return an array with the objects you need.
NSArray* arr=[[NSArray alloc] initWithObjects:#"10",#"11",#"10",#"2", nil];
NSLog(#"%d",[[arr filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF == '2'"]] count]);
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
canonical way to randomize an NSArray in Objective-C
Suppose I have an array as follows.
shuffleArray = [[NSMutableArray alloc] initWithObjects:#"A",#"B",#"C",#"D",#"E", nil];
and I want to change the position of elements of the array randomly as follows:
shuffleArray = [[NSMutableArray alloc] initWithObjects:#"C",#"A",#"B",#"E",#"D", nil];
then how can I do this?
-(NSArray *)shuffleme
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
NSMutableArray *array1 = [self mutableCopy];
while ([array1 count] > 0)
{
int temp = arc4random() % [array1 count];
id objectToMove = [array1 objectAtIndex:temp];
[array addObject:objectToMove];
[array1 removeObjectAtIndex:temp];
}
[array1 release];
return array;
}
Hope, this will help you..
-(void)changeObjectAtIndex:(int)index1 index2:(int)index2 array:(NSMutableArray *)array
{
id objectAtIndex1=[array objectAtIndex:index1];
[array insertObject:[array objectAtIndex:index2] atIndex:index1];
[array insertObject:id atIndex:index2];
}
This is For swapping objects at two indexes and you can make this function recursive if you know exactly where you want particular objects.But if you want it randomly then you can adopt Nit's Method.
Randomly can also be done by:-
id newObject= [[[yourArray objectAtIndex:index] retain] autorelease];
[yourArray removeObjectAtIndex:index];
[yourArray insertObject:object atIndex:yourIndex];
keep in mind to retain the object.
I have an array of myObjects called arrayToFilter. One (element?) of myObject is an array of bezierpaths. I am comparing the bezierpath at a particular index (thispath) to a second path, and making filteredArray composed of only those objects in which the paths overlapped a certain amount (20%). I used indexedOfObjectsPassingTest to like this:
NSIndexSet * index = [[arrayToFilter objectsAtIndexes:index] indexesOfObjectsPassingTest:^BOOL (id obj, NSUInteger idx, BOOL *stop){
MyObject * anobject = obj;
UIBezierPath * thispath = [anobject.allPaths objectAtIndex:i];
NSInteger overlap = [self percentPathBoxOverlap:path: thispath];
return overlap>20;
}];
if ([index count] !=0){
filteredArray = [arrayToFilter objectsAtIndexes:index] ;
}
This works fine. But what i'd like to do is have filteredArray come out sorted with those object with the higher value for overlap coming out first. since overlap is calculated on the fly based on the current path and thispath, i don't know how to use any of the sorted array methods.
You can start off by creating an array of dictionaries containing both path and the overlap data. This will require some modification to your current approach where you search and extract over filter.
NSMutableArray * searchResults = [NSMutableArray array];
[arrayToSearch enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
MyObject * anobject = obj;
UIBezierPath * thispath = [anobject.allPaths objectAtIndex:i];
NSInteger overlap = [self percentPathBoxOverlap:path: thispath];
if ( overlap > 20 ) {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:anObject, #"object", [NSNumber numberWithInteger:overlap], #"overlap", nil];
[searchResults addObject:dictionary];
}
}];
Now you can sort this array using the overlap key of the dictionaries.
NSSortDescriptor * descriptor = [NSSortDescriptor sortDescriptorWithKey:#"overlap" ascending:NO];
NSArray * sortedArray = [searchResults sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];
Now sortedArray will have the sorted list of path and overlap information.
You'll have to sort the array first, then call indexesOfObjectsPassingTest for the sorted indices. sortedArrayUsingComparator: is one of the easier methods of sorting an array, it takes a block just like the indexesOfObjectsPassingTest method.
NSArray arrayToFilter = [originalArray sortedArrayUsingComparator: ^(id a, id b)
{
if (a.someValue > b.someValue) return NSOrderedAscending;
if (a.someValue < b.someValue) return NSOrderedDescending;
return NSOrderedSame;
}];
Then you can perform your existing filtering on the arrayToFilter
If you have an NSMutableArray with three NSDictionarys like this:
{
name:steve, age:40;
name:steve, age:23;
name:paul, age:19
}
How do I turn that into an array with just two strings { steve, paul }. In other words, the unique names from the original NSMutableArray? Is there a way to do this using blocks?
Similar to the other answer, you could also do:
NSSet * names = [NSSet setWithArray:[myArray valueForKey:#"name"]];
Or
NSArray * names = [myArray valueForKeyPath:#"#distinctUnionOfObjects.name"];
something like that:
NSMutableSet* names = [[NSMutableSet alloc] init];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)) {
[names addObject:[obj valueForKey:#"name"]];
}];
[names allObjects] will return a NSArray of unique name