I have the following categoryNames array.
And now, I have categoryTempElements and it has cName property. I need to know how to order categoryTempElements with a order of categoryNames.
UPDATE: I have added sortOrder property to Category object and tried the following but order does not change.
for (Category* a in categoryTempElements) {
int index = (int)[categoryNames indexOfObject:a.cName];
a.sortOrder = index;
}
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray = [categoryTempElements sortedArrayUsingDescriptors:sortDescriptors];
Another way could be without using sortedArrayUsingComparator, using two for-loops. Declare a new Mutable array called sortedCategoryElements and compare the categoryNames in categoryTempElements, If matches add it to a new array sortedCategoryElements:
NSMutableArray *sortedCategoryElements = [NSMutableArray new];
for (NSString *name in categoryNames) {
for (Category *category in categoryTempElements) {
if (name == category.cName) {
[sortedCategoryElements addObject:category];
break;
}
}
}
I tried with your set of data, it worked for me.
Hope it helps!
You need first convert your categoryNames array into dictionary with NSString key and NSNumber int value, the value will be the order in the array
//this is example code, this will be your first array (reference value array)
NSArray * array = #[#"prueba",#"prueba2",#"prueba3"];
//first you need convert this array in NSDictionary
NSMutableDictionary * arrayDict = [NSMutableDictionary dictionary];
int counter = 0;
for (NSString * value in array) {
if(arrayDict[value] == nil)
{
arrayDict[value] = [NSNumber numberWithInt:counter];
}
counter++;
}
After that then you can get the value and order with sortedArrayUsingComparator method, something like this
//this is an example of your second array categoryTempElements
NSArray * arrayOfObjs = #[[testObject testObjectWithName:#"prueba3"],[testObject testObjectWithName:#"prueba"],[testObject testObjectWithName:#"prueba2"]];
NSArray * sorted = [arrayOfObjs sortedArrayUsingComparator:^NSComparisonResult(testObject * _Nonnull obj1, testObject * _Nonnull obj2) {
if([((NSNumber*)arrayDict[obj1.cName]) intValue] < [((NSNumber*)arrayDict[obj2.cName]) intValue]){
return NSOrderedAscending;
}
if([((NSNumber*)arrayDict[obj1.cName]) intValue] > [((NSNumber*)arrayDict[obj2.cName]) intValue]){
return NSOrderedDescending;
}
return NSOrderedSame;
}];
for (testObject * obj in sorted) {
NSLog(#"%#",obj.cName);
}
And voila in sorted you will have your array of object sorted by your first array NSString order
Hope this helps
Create a dictionary with cName as the key and the Category object as the value. Then iterate over the categoryNames array, and build another array by using each item in categoryNames as the key. The resulting array should be sorted in the same order as categoryNames.
NSArray has a method sortedArrayUsingComparator: which sorts an array using the ordering implemented by the block you pass. This block, of type NSComparator, is passed references to two elements of your array and you must return the order of those two elements.
And now, I have categoryTempElements and it has cName property.
So your block will be passed two categoryTempElements, you need to access the cName property of each, and compare the resulting two values...
I need to know how to order categoryTempElements with a order of categoryNames
by the position, i.e. the index, of those values in your categoryNames array. The method indexOfObject: provides that index for you.
So put that together and your problem is solved.
HTH
Related
I am working on an iOS app, where I will be getting a JSON Object from server, which will be populated on a UITableView.
User can change values on tableview, Hence resulting in a new JSON.
Now I want to send only delta (Difference of Two JSON Objects) back to server.
I know I can traverse both Objects for finding delta. But just wish to know is there any easy solution for this problem.
Ex:
NSDictionary *dict1 = {#"Name" : "John", #"Deptt" : #"IT"};
NSDictionary *dict2 = {#"Name" : "Mary", #"Deptt" : #"IT"};
Delta = {#"Name" : "Mary"}
Considering new value is Mary for key name;
Thanks In Advance
isEqualToDictionary: Returns a Boolean value that indicates whether the contents of the receiving dictionary are equal to the contents of another given dictionary.
if ([NSDictionary1 isEqualToDictionary:NSDictionary2) {
NSLog(#"The two dictionaries are equal.");
}
Two dictionaries have equal contents if they each hold the same number of entries and, for a given key, the corresponding value objects in each dictionary satisfy the isEqual: test.
Here's how to get all the keys with non-matching values. What to do with those keys is app level question, but the most informative structure would include an array of mismatched values from both dictionaries, as well has handle keys from one that are not present in the other:
NSMutableDictionary *result = [#{} mutableCopy];
// notice that this will neglect keys in dict2 which are not in dict1
for (NSString *key in [dict1 allKeys]) {
id value1 = dict1[key];
id value2 = dict2[key];
if (![value1 equals:value2]) {
// since the values might be mismatched because value2 is nil
value2 = (value2)? value2 : [NSNull null];
result[key] = #[value1, value2];
}
}
// for keys in dict2 that we didn't check because they're not in dict1
NSMutableSet *set1 = [NSMutableSet setWithArray:[dict1 allKeys]];
NSMutableSet *set2 = [NSMutableSet setWithArray:[dict2 allKeys]];
[set2 minusSet:set1]
for (NSString *key in set2) {
result[key] = #[[NSNull null], dict2[key]];
}
There are certainly more economical ways to do it, but this code is optimized for instruction.
Just enumerate through and compare the dictionaries key-by-key. This will output any differences as well as any unmatched keys on either side, you can tweak the logic depending on exactly what you want to include.
- (NSDictionary *)delta:(NSDictionary *)dictionary
{
NSMutableDictionary *result = NSMutableDictionary.dictionary;
// Find objects in self that don't exist or are different in the other dictionary
[self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
id otherObj = dictionary[key];
if (![obj isEqual:otherObj]) {
result[key] = obj;
}
}];
// Find objects in the other dictionary that don't exist in self
[dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
id selfObj = self[key];
if (!selfObj) {
result[key] = obj;
}
}];
return result;
}
I have an array having objects with different properties. I want to create an array of sets which contain objects with same value of a single property of the object.
Suppose this is an array of object which has property a and b
1: {a:10, b:5}, 2: {a:2,b:5}, 3: {a:20,b:5}, 4: {a:5,b:5}, 5: {a:4,b:20}, 6: {a:51,b:20}
I want to create another array of NSSet of objects with distinct values of property b
so the result would be the following Array of 2 NSSet
1: {a:10, b:5}, {a:2,b:5}, {a:20,b:5}, {a:5,b:5}
2: {a:4,b:20}, {a:51,b:20}
How can this be done?
I'd do this by first creating a dictionary of sets where the keys of the dictionary are the unique values of "b".
Note: This is untested code. There could be typos here.
NSArray *objectArray = ... // The array of "SomeObject" with the "a" and "b" values;
NSMutableDictionary *data = [NSMutableDictionary dictionary];
for (SomeObject *object in objectArray) {
id b = object.b;
NSMutableSet *bSet = data[b];
if (!bSet) {
bSet = [NSMutableSet set];
data[b] = bSet;
}
[bSet addObject:object];
}
NSArray *setArray = [data allValues];
setArray will contain your array of sets.
This codes also assumes you have a sane isEqual: and hash implementation on your SomeObject class.
This is how you can do this:
NSArray *data = #[#{#"a":#10, #"b":#5}, #{#"a":#2,#"b":#5}, #{#"a":#4,#"b":#20}, #{#"a":#51,#"b":#20}];
NSSet *bSet = [NSSet setWithArray: [data valueForKey: #"b"]];
NSMutableArray *filteredArray = [[NSMutableArray alloc] initWithCapacity: bSet.count];
for (NSNumber *bValue in bSet) {
NSPredicate *anArrayFilterPredicate = [NSPredicate predicateWithBlock:^BOOL(NSDictionary *aDictionaryData, NSDictionary *bindings) {
if ([aDictionaryData[#"b"] isEqual:bValue]) {
return YES;
}
return NO;
}];
NSArray *uniqueBValueArray = [data filteredArrayUsingPredicate:anArrayFilterPredicate];
[filteredArray addObject:uniqueBValueArray];
}
NSLog(#"filteredArray = %#", filteredArray);
Using Key-Value coding collection operators, we can get the array of distinct values for an object. Then you could easily compute the results you want.
In your case this could be done like this.
NSArray *arrayOfDistinctObjects = [array valueForKeyPath:#"#distinctUnionOfObjects.b"];
NSMutableArray *newSetArray = [NSMutableArray new];
for (id value in arrayOfDistinctObjects) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.b == %#", value];
NSArray *filterArray = [array filteredArrayUsingPredicate:predicate];
NSSet *newSet = [NSSet setWithArray:filterArray];
[newSetArray addObject:newSet];
}
where array is the array of objects on which you wanna operate.
arrayOfDistinctObjects gives you the array of distinct values for b.
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.
I am trying to copy the objects content of a NSDictionary to a NSMutableArray, and I am using the following code :
// Use when fetching binary data
NSData *responseData = [request responseData];
// View the data returned - should be ready for parsing.
resultsDictionary = [responseData objectFromJSONData];
NSLog(#"ResultsDictionary:%#", resultsDictionary);
self.OnlineObjects = [[[NSMutableArray alloc] init] autorelease];
for (NSDictionary * dataDict in resultsDictionary) {
[OnlineObjects insertObject:dataDict atIndex:0];
}
NSLog(#"OnlineObjects:%#", OnlineObjects);
This is working as i am getting all objects from the Dictionary, but the objects order have been revers, first object is now last ...
How can tell the insertObject to add the object at the last index ?
Thanks
You can use the addObject: method instead.
To get rid of the hash order problem get allKeys, sort the array and then use the elements as keys to get the objects in proper order.
Verbose example (for integer keys):
NSArray *indices = [[resultsDictionary allKeys] sortedArrayUsingComparator:^(id obj1, id obj2) {
if ( [obj1 intValue] > [obj2 intValue] ) {
return (NSComparisonResult)NSOrderedDescending;
}
if ( [obj1 intValue] < [obj2 intValue] ) {
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}];
for (int i = 0; i < [indices count]; i++) {
NSDictionary *obj = [resultsDictionary objectForKey:[indices objectAtIndex:i]];
[OnlineObjects addObject:obj];
}
The order of the elements in a NSDictionary is undefined, you don't know in which order they will be retrieved from the dictionary. The only way to do have the array sorted is to sort it once all the values from the dictionary are transferred to the array.
Two things you should know:
NSDictionary is a key-value container, which does not guarantee the order of the objects. You have no way to ensure that the order of inserting will be mantained when reading by using this data structure. Check other strategies if order is important for you, but do not rely on NSDictionary for this.
You have a couple of methods to extract the info of the keys and data: allKeys and allValues. Use them instead of creating your own.
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