I have an NSArray of Object Classes which consist of two textfields. I would like to sort these objects in ascending order, I have done this with NSDictionary objects before however I have now changed then to an Object Class that I have made so I dont really know how to compare the values to get the sorted array.
The object variables are NSNumbers but contain only number values, which I think will effect things.
This is how I was sorted the NSDictionary value with my old code.
NSArray *tempSortedItemsArray = [installArray sortedArrayUsingDescriptors:
#[[NSSortDescriptor
sortDescriptorWithKey:#"first" ascending:YES],
[NSSortDescriptor
sortDescriptorWithKey:#"second" ascending:YES]]];
sortedItemsArray = [tempSortedItemsArray mutableCopy];
tempSortedItemsArray = nil;
So if I have an array of object like this
(first / second)
2 1
0 0
1 0
1 1
2 2
2 0
3 0
if would sort like this
0 0
1 0
1 1
2 0
2 1
2 2
3 0
any help adjusting this for NSObject class with NSNumber variables first and second would be greatly appreicated.
[arr sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSString *first=[NSString stringWithFormat:#"%d%d",obj1.num1.intValue,obj1.num2.intValue];
NSString *second=[NSString stringWithFormat:#"%d%d",obj2.num1.intValue,obj2.num2.intValue];
return [first compare:second options:NSNumericSearch];
}];
You can use the other sort methods of NSArray such as:
sortedArrayUsingFunction:context:
sortedArrayUsingSelector:
sortedArrayUsingComparator:
Given an Object that works like this:
#interface Object : NSObject
#property (copy) NSNumber *first;
#property (copy) NSNumber *second;
+(Object *)objectWithWithFirst:(NSNumber *)first second:(NSNumber *)second;
#end
#implementation Object
+(Object *)objectWithWithFirst:(NSNumber *)first second:(NSNumber *)second {
Object *object = [[Object alloc] init];
object.first = first;
object.second = second;
return object;
}
-(NSString *)description {
return [NSString stringWithFormat:#"%# %#", _first, _second];
}
#end
Your could sort with
NSArray *sorted = [array sortedArrayUsingComparator:^NSComparisonResult(Object *obj1, Object *obj2) {
NSComparisonResult result = [obj2.second compare:obj2.second];
if (result == NSOrderedSame)
result = [obj1.first compare:obj2.first];
return result;
}];
Example:
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSArray *array = #[
[Object objectWithWithFirst:#2 second:#1],
[Object objectWithWithFirst:#0 second:#0],
[Object objectWithWithFirst:#1 second:#0],
[Object objectWithWithFirst:#1 second:#1],
[Object objectWithWithFirst:#2 second:#2],
[Object objectWithWithFirst:#2 second:#0],
[Object objectWithWithFirst:#3 second:#0]
];
NSLog(#"%#", array);
NSArray *sorted = [array sortedArrayUsingComparator:^NSComparisonResult(Object *obj1, Object *obj2) {
NSComparisonResult result = [obj2.second compare:obj2.second];
if (result == NSOrderedSame)
result = [obj1.first compare:obj2.first];
return result;
}];
NSLog(#"%#", sorted);
}
return 0;
}
Which will work perfectly on your sample data. Of course, you could put the comparator code directly into your Object class - cleaning thins up even more.
If your custom class has the properties first and second then the code you've posted will just work.
NSNumbers already know how to compare themselves to other numbers. NSSortDesvriptors work with any object that implements the reading part of key-value coding. All NSObject subclasses automatically do so for any property that follows ordinary conventions. #propertys follow ordinary conventions.
Related
Let's assume I have this NSDictionary:
NSDictionary *d = #{
#"124":#[#"-40",#"1489365614.248664"],
#"130":#[#"-40",#"1489365604.258358"],
#"134":#[#"-40",#"1489365615.49739"],
#"53":#[#"-40",#"1489365610.502131"],
#"57":#[#"-40",#"1489365609.253352"],
#"73":#[#"-40",#"1489365608.004844"],
#"89":#[#"-44",#"1489365611.750874"],
#"91":#[#"-64",#"1489365606.755874"],
#"93":#[#"-45",#"1489365605.507149"],
#"96":#[#"-45",#"1489365613.000054"]
};
I can sort it by the first value of the array like this:
NSArray *sortedKeys = [d keysSortedByValueUsingComparator: ^(NSArray *obj1, NSArray *obj2) {
return (NSComparisonResult)[obj1[0] compare:obj2[0]];
}];
And will return this array:
#[73,134,53,124,130,57,89,96,93,91]
Which translates to the dictionary been like this:
#{
#"73": #[#"-40",#"1489365608.004844"],
#"134":#[#"-40",#"1489365615.49739"],
#"53": #[#"-40",#"1489365610.502131"],
#"124":#[#"-40",#"1489365614.248664"],
#"130":#[#"-40",#"1489365604.258358"],
#"57": #[#"-40",#"1489365609.253352"],
#"89": #[#"-44",#"1489365611.750874"],
#"96": #[#"-45",#"1489365613.000054"],
#"93": #[#"-45",#"1489365605.507149"],
#"91": #[#"-64",#"1489365606.755874"]
};
Now, as you might imagine, the second value of the array in the dictionary, is a timestamp. And if the first values are equal, I'd like to sort by this timestamp so I can get the newest one first.
If I was able to do that, I should be getting back an array like this:
#[134,124,53,57,73,130,89,96,93,91]
Which would translate the dictionary to actually be like this:
#{
#"134":#[#"-40",#"1489365615.49739"],
#"124":#[#"-40",#"1489365614.248664"],
#"53": #[#"-40",#"1489365610.502131"],
#"57": #[#"-40",#"1489365609.253352"],
#"73": #[#"-40",#"1489365608.004844"],
#"130":#[#"-40",#"1489365604.258358"],
#"89": #[#"-44",#"1489365611.750874"],
#"96": #[#"-45",#"1489365613.000054"],
#"93": #[#"-45",#"1489365605.507149"],
#"91": #[#"-64",#"1489365606.755874"]
};
Hope it makes sense and someone has an answer.
Thanks
Use this logic:
NSArray *sort = [d keysSortedByValueUsingComparator:^(NSArray *obj1, NSArray *obj2) {
NSComparisonResult result = [obj1[0] compare:obj2[0]];
if (result == NSOrderedSame) {
result = [obj2[1] compare:obj1[1]];
}
return result;
}];
If there's a tie, just compare the second values...
NSArray *sortedKeys = [d keysSortedByValueUsingComparator: ^(NSArray *obj1, NSArray *obj2) {
NSComparisonResult result = [obj1[0] compare:obj2[0]];
return (result == NSOrderedSame)? [obj2[1] compare:obj1[1]] : result;
}];
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]];
}];
I have an array of objects that I convert to a NSSet:
NSArray *arr = #[#{ #"someProp": #21, #"unnecessaryProp": #"tada" }, ... ];
NSSet *collection = [NSSet setWithArray:arr];
I would like to project the properties I want (by key) out of each object in the set and end up with a new array like:
NSArray *projectedArray = [collection allObjects]; // #[#{ "someProp": #21 }, ... ], "unnecessaryProp" has been removed
Besides enumeration, is there any other way, perhaps NSPredicate?
NOTE: The objects in the array are subclasses of NSObject, in my example I mentioned a NSDictionary
Since NSPredicate does not do projections, you would end up enumerating the set. I would enumerate it with a block, and project the keys in the individual dictionaries like this:
NSArray *keep= #["someProp"];
NSMutableArray *res = [NSMutableArray array];
[collection enumerateObjectsUsingBlock:^(id dict, BOOL *stop) {
NSArray *values = [dict objectsForKeys:keep notFoundMarker:#""];
[res addObject:[NSDictionary dictionaryWithObjects:values forKeys:keep]];
}];
EDIT : (in response to comments)
I should have mentioned that the objects inside the array are subclasses of NSObject and objectsForKeys is not a method.
Then you could use MartinR's suggestion to build a dictionary using KVC:
NSArray *keep= #["someProp"];
NSMutableArray *res = [NSMutableArray array];
[collection enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
[res addObject:[obj dictionaryWithValuesForKeys:keep]];
}];
If you only need the values for one property of the objects in a collection of type NSSet or NSArray or their subclasses, you can use the KVC method valueForKey:
NSArray *dogs = #[#{#"name" : #"Fido",
#"toys" : #[#"Ball", #"Kong"]},
#{#"name" : #"Rover",
#"toys" : #[#"Ball", #"Rope"]},
#{#"name" : #"Spot",
#"toys" : #[#"Rope", #"Kong"]}];
NSArray *vals = [set valueForKey:#"name"];
NSLog(#"%#", vals);
The above code prints the following on the console:
2014-05-16 09:26:58.293 xctest[17223:303] (
Fido,
Rover,
Spot
)
If you need the values of several properties of the objects in the collection, use dictionaryWithValuesForKeys:. Given the same array as in the previous example, the following code...
NSDictionary *dict = [dogs dictionaryWithValuesForKeys:#[#"name", #"toys"]];
NSLog(#"%#", dict);
produces an array of dictionaries, and logs the following output:
2014-05-16 09:35:34.793 xctest[17275:303] {
name = (
Fido,
Rover,
Spot
);
toys = (
(
Ball,
Kong
),
(
Ball,
Rope
),
(
Rope,
Kong
)
);
}
This works regardless of whether the objects in the target collections are instances of NSDictionary or of custom classes.
you can use indexOfObjectPassingTest on your array or NSSet.
__block NSUInteger maxIdex = [_myArrray count]-1;
__block NSMutableIndexSet* objToRemove = [[NSMutableIndexSet alloc]init];
[_myArrray indexOfObjectPassingTest:^(id object, NSUInteger idx, BOOL * stop){
MyObject *obj = (MyObject*)object;
if(....){
[objToRemove addIndex:[_myArrray indexOfObject:obj]];
}
*stop = (idx == maxIdex);
return *stop;
}];
[_myArrray removeObjectsAtIndexes:objToRemove];
I have an NSObject with NSStrings inside. How do I add objects with unique obj.name only to an NSMutableArray? I tried NSOrderedSet but it only works if you add NSString to an array and not objects with NSString inside.
Example.
##interface MyObject : NSObject
#property (strong, nonatomic) NSString *name;
#end
NSMutableArray *array = {MyObject.name,MyObject.name,MyObject.name};
How do I make sure that no two MyObjects have the same name?
Use NSPredicate for seraching object in NSMutableArray if not present then add it to NSMutableArray.
Try this.
NSArray * filtered = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"name = %#", #"MyObject.name"]];
if(![array count])
[array addObject:MyObject ];
All NSSet classes use isEqual: in combination with hash: to compare equality.
Because you have not redefined these simply storing two objects with the same name in a set will be possible as the NSObject implementation of isEqual: and hash: will be used.
The documentation of NSObject Protocol talks about overriding isEqual and hash.
This previous answer on Stackoverflow details how to implement hash and isEqual correctly.
In your own implementation of hash you can use NSString's hash method.
Example
- (NSUInteger) hash {
NSUInteger prime = 31;
NSUInteger result = 1;
result = prime * result + [super hash];
result = prime * result + self.name == nil ? 0 : [self.name hash];
return result;
}
- (bool) isEqual:(id)other {
if (other == self) {
return YES;
}
if (!other || ![other isKindOfClass:[self class]]) {
return NO;
}
return [self.name isEqualToString:other.name];
}
Personally, I would use a NSMutableDictionary with MyObject.name as the key. That way all you would have to do is this:
if( myDictionary[MyObject.name] == nil )
{
myDictionary[MyObject.name] = MyObject;
}
Much more efficient than using a regular NSMutableArray based on the number of additions you are doing. Plus if you want to get access to an array of all the values, all you have to do is:
NSArray *array = [myDictionary allValues];
The Big-O Runtime for NSPredicate is O(n) where the dictionary method is O(1).
I have an NSArray which contains some NSString objects. For example:
NSArray *objects = #[#"Stin",#"Foo",#"Ray",#"Space"];
Now I need to sort this array based on following order of Strings.
NSArray *sortOrder = #[#"John",#"Foo",#"Space",#"Star",#"Ray",#"Stin"];
So the answer should be
NSArray *sorted = #[#"Foo",#"Space",#"Ray",#"Stin"];
How can I achieve this?
ANSWER:
Based on Accepted answer of dasblinkenlight, I did following and it worked to charm.
NSMutableArray *objects = #[#"Star",#"Stin",#"Foo",#"Ray",#"Space",#"John"];
NSArray *sortOrder = #[#"John",#"Foo",#"Space",#"Star",#"Ray",#"Stin"];
[objects sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
int index1 = [sortOrder indexOfObject:obj1];
int index2 = [sortOrder indexOfObject:obj2];
if (index1 > index2)
return NSOrderedDescending;
if (index1 < index2)
return NSOrderedAscending;
return NSOrderedSame;
}];
Create NSComparator that holds a reference to the superset array, and decides the relative order of strings by comparing the results of calling [superset indexOfObject:str] on both strings. Call sortedArrayUsingComparator: passing an instance of NSComparator to get the desired ordering.
dasblinkenlight's solution would work, but like most programming problems there are multiple ways to go about it. Here is one such alternative:
NSMutableArray *sorted = [NSMutableArray arrayWithCapacity:0];
[sortOrder enumerateObjectsUsingBlock:^(NSString *sortedString, NSUInteger idx, BOOL *stop) {
if ([objects containsObject:sortedString]) {
[sorted addObject:sortedString];
}
}];
The variable names correspond to the variable names used in the original question.
This works because the enumeration happens in order. Therefore, what takes place is, essentially, a cull of the objects that exist in both arrays in the order as specified by sortOrder.